Spring boot jpa second level cache not being use even after configuring - spring-boot

We have set up JPA second-level cache but not working with the following configuration cache is configured properly as per logging but it not cache the database result and always fetching data from the database.
11:44:07.454 [main] [UserId: ] WARN net.sf.ehcache.Cache - Cache: com.Record has a maxElementsInMemory of 0. In Ehcache 2.0 this has been changed to mean a store with no capacity limit. Set it to 1 if you want no elements cached in memory.
ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true">
<defaultCache eternal="false" timeToLiveSeconds="10000" memoryStoreEvictionPolicy="LFU" statistics="true" maxElementsInMemory="10000" overflowToDisk="false" />
<cache maxElementsInMemory="10000" name="Record" eternal="false" timeToIdleSeconds="10000" timeToLiveSeconds="10000" memoryStoreEvictionPolicy="LFU" statistics="true" />
</ehcache>
Enabled spring boot Caching in Application.java
#SpringBootApplication
#EnableScheduling
#EnableCaching
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#PostConstruct
public void init(){
// Setting Spring Boot SetTimeZone
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Entity Record.java
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "t_record")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "c_discriminator", discriminatorType = DiscriminatorType.STRING)
#Cacheable
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE,region = "Record")
#NamedQueries({#NamedQuery(name = Record.QUERY_findAllByType, query = "SELECT r FROM Record r WHERE TYPE(r) = ?1 ORDER BY r.date DESC, r.id DESC")})
public abstract class Record implements Cloneable, XmlOutputEntity {
/**
* Class UID
*/
private static final long serialVersionUID = 7146308281386143748L;
public static final String QUERY_findAllByType = "findAllRecordByType";
}
Enabled second-level cache and query cache in the database configuration.
#Configuration
public class DatabaseConfig {
protected PropConfig propConfig;
#Autowired
public DatabaseConfig(PropConfig propConfig) {
this.propConfig = propConfig;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(propConfig.getDatabaseDriver());
dataSource.setUrl(propConfig.getDatabaseUrl());
dataSource.setUsername(propConfig.getDatabaseUsername());
dataSource.setPassword(propConfig.getDatabasePassword());
return dataSource;
}
#Bean
public LocalValidatorFactoryBean validator() {
final LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
return validator;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource);
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.test");
Properties props = new Properties();
props.put("logging.level.org.hibernate.SQL", "true");
props.put("logging.level.org.hibernate.type.descriptor.sql.BasicBinder", "TRACE");
props.put("show-sql", "true");
lef.setJpaProperties(props);
lef.getJpaPropertyMap().put("jadira.usertype.autoRegisterUserTypes", "true");
lef.getJpaPropertyMap().put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
lef.getJpaPropertyMap().put("hibernate.temp.use_jdbc_metadata_defaults", "false");
lef.getJpaPropertyMap().put("hibernate.cache.use_second_level_cache", "true");
lef.getJpaPropertyMap().put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");
lef.getJpaPropertyMap().put("hibernate.cache.use_query_cache", "true");
lef.setSharedCacheMode(SharedCacheMode.ALL);
return lef;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(propConfig.isShowSql());
hibernateJpaVendorAdapter.setGenerateDdl(propConfig.isGenerateDdl());
hibernateJpaVendorAdapter.setDatabase(propConfig.getVendor());
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}

Query caching has to be enabled on a case by case basis by setting the org.hibernate.cacheable hint to true. I think with Spring Data JPA you have to annotate the repository method with #QueryHint(name = "org.hibernate.cacheable", value = "true")

Related

NullPointerException in Spring Autowiring Repository

I'm getting an NPE when I'm trying to auto wiring a Repository in my managed bean. This is my first project trying to use java configurations instead of xml configurations.
I'm not instantiating my self the repository and all the packages are in the spring context, even I can see in the log the instantiating of the repository.
Please, someone, give me a hand to figure out what's missing in my configuration
Using spring version 5.1.0.RELEASE and
<dependency>
<groupId>com.googlecode.genericdao</groupId>
<artifactId>dao-hibernate</artifactId>
<version>1.2.0</version>
</dependency>
1) WebMvcConfig
#Configuration
#EnableWebMvc
#ComponentScan({
"com.config","com.gedificios",
})
public class WebMvcConfig implements WebMvcConfigurer {
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver resolver = new
InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}
2) PersistenceJPAConfig
#Configuration
#EnableTransactionManagement
#PropertySource({"classpath:database.properties"})
#ComponentScan({"com.config","com.gedificios.core", "com.gedificios.persistence", "com.gedificios.view"})
#EntityScan("com.gedificios.persistence.entities")
#EnableJpaRepositories(basePackages = "com.gedificios.core.repository")
public class PersistenceJPAConfig {
#Autowired
private Environment env;
public PersistenceJPAConfig() {
super();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setPersistenceXmlLocation("classpath*:WEB-INF/persistence.xml");
entityManagerFactoryBean.setPersistenceUnitName("persistenceUnit");
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setJpaProperties(additionalProperties());
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabasePlatform(env.getProperty("hibernate.dialect"));
vendorAdapter.setShowSql(Boolean.getBoolean(env.getProperty("hibernate.show_sql")));
vendorAdapter.setGenerateDdl(Boolean.getBoolean(env.getProperty("hibernate.generateDdl")));
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
return entityManagerFactoryBean;
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
return hibernateProperties;
}
#Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("hibernate.connection.driver_class"));
dataSource.setUrl(env.getProperty("hibernate.connection.url"));
dataSource.setUsername(env.getProperty("hibernate.connection.username"));
dataSource.setPassword(env.getProperty("hibernate.connection.password"));
return dataSource;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
#Primary
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Bean
public JPAAnnotationMetadataUtil getMetadataUtil() {
return new JPAAnnotationMetadataUtil();
}
#Bean
public JPASearchProcessor getJPASearchProcessor(JPAAnnotationMetadataUtil jpaAnnotationMetadataUtil) {
return new JPASearchProcessor(jpaAnnotationMetadataUtil);
}
}
3) Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source/>
<class>com.gedificios.persistence.entities.Contrato</class>
<class>com.gedificios.persistence.entities.Edificio</class>
<class>com.gedificios.persistence.entities.ubigeo.Pais</class>
<class>com.gedificios.persistence.entities.ubigeo.Departamento</class>
<class>com.gedificios.persistence.entities.ubigeo.Provincia</class>
<class>com.gedificios.persistence.entities.ubigeo.Distrito</class>
<properties>
<property name="hibernate.dialect" value="${hibernate.dialect}"/>
<property name="hibernate.connection.charSet" value="UTF-8"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.use_jdbc_metadata_defaults" value="false"/>
</properties>
</persistence-unit>
</persistence>
4) PaisRepository
#Repository
public class PaisRepository extends BaseDAO<PaisDTO, String> {
#Autowired
DataSource dataSource;
private static final Logger LOG = LoggerFactory.getLogger(PaisRepository.class);
#PostConstruct
public void init() {
LOG.info("\n/***************** INICIALIZANDO REPOSITORY DE PAÍS /*****************\n");
}
public List<PaisDTO> getAllPaises() {
List< PaisDTO> items = null;
StringBuffer sql = new StringBuffer();
sql.append(" SELECT ");
sql.append(" P.id_pais \"idPais\" , ");
sql.append(" P.nombre_pais \"nombrePais\" ");
sql.append(" FROM ubigeo_pais as P order by p.nombre_pais");
System.out.println(sql);
org.hibernate.Query<PaisDTO> query = em().unwrap(Session.class).createSQLQuery(sql.toString()).setResultTransformer(new JpaHbmBeanResultTransformer(PaisDTO.class));
items = query.list();
return items;
}
}
4.1) Log evidence
06:24:50.197 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getJPASearchProcessor'
06:24:50.198 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getMetadataUtil'
06:24:50.198 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'getJPASearchProcessor' via factory method to bean named 'getMetadataUtil'
06:24:50.199 [localhost-startStop-1] INFO com.gedificios.core.repository.PaisRepository -
/***************** INICIALIZANDO REPOSITORY DE PAÍS /*****************
06:24:50.201 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'DTOMapperServiceDozerImpl'
06:24:50.201 [localhost-startStop-1] DEBUG org.dozer.DozerInitializer - Tried to perform initialization when Dozer was already started.
06:24:50.201 [localhost-startStop-1] INFO org.dozer.DozerBeanMapper - Initializing a new instance of dozer bean mapper.
5) ManagedBean
#ManagedBean
#Scope("view")
#Component
public class AdminEdificios extends FindCrudBeanBase {
#Autowired
ApplicationContext applicationContext;
#Autowired
PaisRepository paisRepository;
private static final Logger LOG = LoggerFactory.getLogger(AdminEdificios.class);
private List<PaisDTO> listaPaises;
private List<DepartamentoDTO> listaDepartamentos;
private List<ProvinciaDTO> listaDistritos;
private List<DistritoDTO> listaProvincias;
private String codigoPaisSeleccionado;
private String codigoDepartamentoSeleccionado;
private String codigoProvinciaSeleccionada;
private String codigoDistritoSeleccionado;
#PostConstruct
public void init() {
listaPaises = paisRepository.findAll();
listaDepartamentos = new ArrayList<DepartamentoDTO>();
listaDepartamentos.add(new DepartamentoDTO("001","001","Lima"));
}
...
getters & setters
}
The NPE is exactly here
listaPaises = paisRepository.findAll();

Spring boot Read write Split / Master - Slave / Multiple Databases

I am following this link
https://github.com/kwon37xi/replication-datasource
I have implemented the code But STILL Both my service functions are using the same Database(one which is marked primary)
Service Class
public class TableService{
#Autowired
private Table1Repo t1Repo;
#Transactional(readOnly = false)
public void saveTable1(Table1 t,int a, Table1 t2){
try{
t1Repo.save(t2);
}
catch(Exception e){
System.out.println("Inside");
}
}
#Transactional(readOnly = true)
public Table1 getTable(int id){
return t1Repo.findOne(id);
}
}
Then Added two Class(from the link)
ReplicationRoutingDataSource
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
String dataSourceType = TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "read" : "write";
return dataSourceType;
}
}
WithRoutingDataSourceConfig
#Configuration
public class WithRoutingDataSourceConfig {
/*#Bean(destroyMethod = "shutdown")*/
#Bean
#Primary
#ConfigurationProperties(prefix="datasource.primary")
public DataSource writeDataSource() {
/* EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
.setName("routingWriteDb")
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("classpath:/writedb.sql");
return builder.build();*/
return DataSourceBuilder.create().build();
}
/* #Bean(destroyMethod = "shutdown")*/
#Bean
#ConfigurationProperties(prefix="datasource.secondary")
public DataSource readDataSource() {
/*EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
.setName("routingReadDb")
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("classpath:/readdb.sql");
return builder.build();*/
return DataSourceBuilder.create().build();
}
/**
* {#link org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource}는
* {#link org.springframework.beans.factory.InitializingBean}을 구현하므로,
* 명시적으로 afterPropertiesSet()메소드를 호출하거나
* 별도 #Bean으로 만들어 Spring Life Cycle을 타도록 해야 한다.
*/
#Bean
public DataSource routingDataSource(#Qualifier("writeDataSource") DataSource writeDataSource, #Qualifier("readDataSource") DataSource readDataSource) {
ReplicationRoutingDataSource routingDataSource = new ReplicationRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
dataSourceMap.put("write", writeDataSource);
dataSourceMap.put("read", readDataSource);
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(writeDataSource);
return routingDataSource;
}
/**
* {#link org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy}로 감싸서
* 트랜잭션 동기화가 이루어진 뒤에 실제 커넥션을 확보하도록 해준다.
*
* #param routingDataSource
* #return
*/
#Bean
public DataSource dataSource(#Qualifier("routingDataSource") DataSource routingDataSource) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
}
application.prop file
server.port=8089
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.show_sql=true
# Primary DataSource configuration
datasource.primary.url=jdbc:mysql://127.0.0.1:3306/jpa
datasource.primary.username=root
datasource.primary.password=root
# Any of the other Spring supported properties below...
# Secondary DataSource configuration
datasource.secondary.url=jdbc:mysql://127.0.0.1:3306/jpa2
datasource.secondary.username=root
datasource.secondary.password=root
Repository
public interface Table1Repo extends JpaRepository<Table1, Integer>{}
Issue is my both service functions are using the primary Database. What am I missing. I only have these class. Rest I have one Controller
Edited
I have made by code work by adding this class
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="com.example")
public class ReplicationDataSourceApplicationConfig {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(#Qualifier("dataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setDataSource(dataSource);
emfb.setPackagesToScan("com.example");
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
emfb.setJpaVendorAdapter(jpaVendorAdapter);
return emfb;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
I'm the writer of the link you refered.
Which data source do you use with Table1Repo?
You have to inject the specific bean "dataSource" to you JDBC call.
I guess #Primary "writeDataSource" is injected to your Repository.
Try to change #Primary to "dataSource" or find a way to inject "dataSource" to your repository.
you can also try in this way:
Spring Boot 2 with Multiple DataSource for Postgres Data Replication
and here is source code in github:
spring-boot-multi-data-source
The following link explans, how you can have multiple datasource
DB1 (write):
#Configuration
#ConfigurationProperties("spring.datasource-write")
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryWrite",
transactionManagerRef = "transactionManagerWrite",
basePackages = {"com.ehsaniara.multidatasource.repository.writeRepository"}
)
public class DataSourceConfigWrite extends HikariConfig {
public final static String PERSISTENCE_UNIT_NAME = "write";
#Bean
public HikariDataSource dataSourceWrite() {
return new HikariDataSource(this);
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryWrite(
final HikariDataSource dataSourceWrite) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(dataSourceWrite);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(MODEL_PACKAGE);
setJpaProperties(JPA_PROPERTIES);
}};
}
#Bean
public PlatformTransactionManager transactionManagerWrite(EntityManagerFactory entityManagerFactoryWrite) {
return new JpaTransactionManager(entityManagerFactoryWrite);
}
}
DB2 (read):
#Configuration
#ConfigurationProperties("spring.datasource-read")
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryRead",
transactionManagerRef = "transactionManagerRead",
basePackages = {"com.ehsaniara.multidatasource.repository.readRepository"}
)
public class DataSourceConfigRead extends HikariConfig {
public final static String PERSISTENCE_UNIT_NAME = "read";
#Bean
public HikariDataSource dataSourceRead() {
return new HikariDataSource(this);
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryRead(
final HikariDataSource dataSourceRead) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(dataSourceRead);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(MODEL_PACKAGE);
setJpaProperties(JPA_PROPERTIES);
}};
}
#Bean
public PlatformTransactionManager transactionManagerRead(EntityManagerFactory entityManagerFactoryRead) {
return new JpaTransactionManager(entityManagerFactoryRead);
}
}

Spring #Cacheable doesn't work

I have a service with next methods:
public Optional<Test> getTestWithId100() {
return get(100);
}
#Cacheable(value = "test", key = "'1'")
public Optional<Test> get(long id) {
log.error("not from cache");
return testRepository.findOneById(id);
}
I call method getTestWithId100 from controller, but it only get fresh value.
#Slf4j
#Configuration
#EnableCaching
#AutoConfigureAfter(value = { MetricsConfiguration.class, DatabaseConfiguration.class })
public class CacheConfiguration {
#PersistenceContext
private EntityManager entityManager;
private final MetricRegistry metricRegistry;
private net.sf.ehcache.CacheManager cacheManager;
#Inject
public CacheConfiguration(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
#PreDestroy
public void destroy() {
log.info("Remove Cache Manager metrics");
SortedSet<String> names = metricRegistry.getNames();
names.forEach(metricRegistry::remove);
log.info("Closing Cache Manager");
cacheManager.shutdown();
}
#Bean
public CacheManager cacheManager(Properties properties) {
log.debug("Starting Ehcache");
cacheManager = net.sf.ehcache.CacheManager.create();
cacheManager.getConfiguration().setMaxBytesLocalHeap(properties.getCache().getEhcache().getMaxBytesLocalHeap());
log.debug("Registering Ehcache Metrics gauges");
entityManager.getMetamodel().getEntities().forEach(entity -> {
String name = entity.getName();
if (name == null || entity.getJavaType() != null)
name = entity.getJavaType().getName();
Assert.notNull(name, "entity cannot exist without a identifier");
net.sf.ehcache.Cache cache = cacheManager.getCache(name);
if (cache != null)
cacheManager.replaceCacheWithDecoratedCache(cache, InstrumentedEhcache.instrument(metricRegistry, cache));
});
EhCacheCacheManager ehCacheManager = new EhCacheCacheManager();
ehCacheManager.setCacheManager(cacheManager);
return ehCacheManager;
}
}
part of ehcache.xml:
<cache name="test" eternal="true"/>
Why it doesn't work? I tried with different keys but without success.
Spring annotations work by proxying / enhancing your classes. The one limitation in this system is when you call a method on the same bean, then that call is not intercepted by the system and thus no annotation based modifications will be applied.
I think that you are wrong in cache config, let's consider code below (it works fine for me):
#Bean
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheCacheManager().getObject());
}
#Bean
public EhCacheManagerFactoryBean ehCacheCacheManager() {
EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
cmfb.setShared(true);
return cmfb;
}
And of course, ehcache.xml:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true"
monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir" />
<cache name="movieFindCache"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
This config must help you, if it dosn't solve the problem, notify me. HTH

SPRING BOOT access to entityManager

I'm trying to update a project made with spring and hibernate with springboot.
Everything seems ok, but I can't figure how to get the entityManager.
I'm not using spring data, so no domain nor repository is used.
In this project, services are annotated with #service and entityManager is autowire with: #PersistenceContext,
here an example of my service
#Service
#Transactional
public class UserServiceImpl implements UserDetailsService, UserService {
#PersistenceContext
EntityManager entityManager;
/**
*
*/
private static final long serialVersionUID = 6384460058124202695L;
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
User user = entityManager.find(User.class, username);
return user;
}
The problem here is that the entityManager is null. So I read in doc that if we want to manage the entityManager we have to configure it, so I did this in configuration file:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "services" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
The application is launching, but still have the entityManager null.
Any clue?
Edit: adding my application class (Has it's a demo project, I put all the config in one file)
#Configuration
#EnableAutoConfiguration
#ComponentScan
#EnableWebMvcSecurity
public class Application extends WebSecurityConfigurerAdapter{
#Value("${sec.cas.server}")
private String casServer;
#Value("${sec.app.server}")
private String appServer;
#Value("${spring.datasource.driverClassName}")
private String databaseDriverClassName;
#Value("${spring.datasource.url}")
private String databaseUrl;
#Value("${spring.datasource.username}")
private String databaseUsername;
#Value("${spring.datasource.password}")
private String databasePassword;
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
#Bean
public ComboPooledDataSource dataSource() {
ComboPooledDataSource datasource = new ComboPooledDataSource();
try {
datasource.setDriverClass(databaseDriverClassName);
} catch (PropertyVetoException e) {
throw new IllegalArgumentException("Wrong driver class");
}
datasource.setJdbcUrl(databaseUrl);
datasource.setUser(databaseUsername);
datasource.setPassword(databasePassword);
datasource.setAcquireIncrement(1);
datasource.setIdleConnectionTestPeriod(600);
datasource.setMaxPoolSize(500);
datasource.setMinPoolSize(50);
datasource.setMaxStatements(0);
datasource.setMaxConnectionAge(600);
datasource.setMaxIdleTime(600);
return datasource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
DefaultPersistenceUnitManager unitManager = new DefaultPersistenceUnitManager ();
unitManager.setDefaultDataSource(dataSource());
unitManager.setPersistenceXmlLocations("classpath*:META-INF/persistence.xml"); //location of your persistence.xml file
unitManager.setPackagesToScan(new String[] { "services" });
unitManager.setDefaultPersistenceUnitName("entityManager");
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitManager(unitManager);
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.show_sql", "false");
properties.setProperty("hibernate.format_sql", "true");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle9iDialect");
return properties;
}
#Bean
public JdbcDaoImpl jdbcUserService() {
JdbcDaoImpl jdbcDaoImpl = new JdbcDaoImpl();
jdbcDaoImpl.setEnableGroups(true);
jdbcDaoImpl.setEnableAuthorities(true);
jdbcDaoImpl.setDataSource(dataSource());
return jdbcDaoImpl;
}
#Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService("http://"+appServer+"/j_spring_cas_security_check");
serviceProperties.setSendRenew(false);
return serviceProperties;
}
#Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(authenticationUserDetailsService());
casAuthenticationProvider.setUserDetailsService(new UserServiceImpl());
//casAuthenticationProvider.setAuthenticationUserDetailsService(userServiceImpl.class);
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setKey("an_id_for_this_auth_provider_only");
return casAuthenticationProvider;
}
#Bean
public UserDetailsByNameServiceWrapper authenticationUserDetailsService() {
UserDetailsByNameServiceWrapper userDetailsByName = new UserDetailsByNameServiceWrapper();
userDetailsByName.setUserDetailsService(jdbcUserService());
return userDetailsByName;
}
#Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
Cas20ServiceTicketValidator casServiceTicketValidator = new Cas20ServiceTicketValidator("https://"+casServer+"/cas");
casServiceTicketValidator.setProxyCallbackUrl("http://"+appServer+"/");
casServiceTicketValidator.setProxyGrantingTicketStorage(new ProxyGrantingTicketStorageImpl());
return casServiceTicketValidator;
}
#Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
casAuthenticationFilter.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());
casAuthenticationFilter.setProxyGrantingTicketStorage(new ProxyGrantingTicketStorageImpl());
casAuthenticationFilter.setProxyReceptorUrl("/secure/receptor");
return casAuthenticationFilter;
}
#Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl("https://"+casServer+"/cas/login");
casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
return casAuthenticationEntryPoint;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilter(casAuthenticationFilter());
http
.exceptionHandling()
.authenticationEntryPoint(casAuthenticationEntryPoint());
http
.authorizeRequests()
.antMatchers("/", "/home").authenticated()
.anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(casAuthenticationProvider());
}
Your EntityManager being null indicates that Spring isn't processing #PersistenceContext annotations in your UserServiceImpl instance.
You're creating an instance of UserServiceImpl manually as part of configuring your CasAuthenticationProvider bean. That means that Spring knows nothing about the instance and will not inject any of its dependencies. You need to use a Spring-created instance, for example by making it an argument of the casAuthenticationProvider() method.
Try:
DefaultPersistenceUnitManager unitManager = new DefaultPersistenceUnitManager();
unitManager.setPersistenceXmlLocations("classpath*:META-INF/persistence.xml"); //location of your persistence.xml file.
unitManager.setDefaultDataSource(dataSource);
In persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="entityManager" transaction-type="RESOURCE_LOCAL">
//classes, etc...
</persistence-unit>
</persistence>
and:
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitManager(unitManager);
em.setPersistenceUnitName("entityManager");
And then injet:
#PersistenceContext(unitName = "entityManager")
private EntityManager entityManager;
That should work.
Solution by OP.
Got it finally worked, no entityManager is needed to be redefined, just add the bean who get the #persistenceContext (before spring boot 1.2.0)
Here is my application context:
#Configuration
#ComponentScan(basePackages={"services","model"})
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
#Bean
public PersistenceAnnotationBeanPostProcessor persistenceBeanPostProcessor() {
return new PersistenceAnnotationBeanPostProcessor();
}

JPA with Spring MVC Configured via Annotations

I am trying to create a Spring MVC application leveraging JPA for its persistence layer. Unfortunately, I was getting a NullPointerException when accessing the EntityManager as Spring does not appear to be injecting it. My configuration is all annotation-based with #EnableWebMvc. After some searching, I added #Transactional on my DAO and #EnableTransactionManagement on my #Configuration class. Then I got an error about not having a DataSource. Supposedly, a class with #EnableTransactionManagement needs to implement TransactionManagementConfigurer. However, I am having problems figuring out how to create the DataSource as well as why it cannot get it from my persistence.xml.
I would greatly appreciate any help in trying to get the EntityManager injected into my DAO.
My #Configuration class
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.example.myapp")
public class MvcConfig extends WebMvcConfigurerAdapter
implements TransactionManagementConfigurer {
private static final boolean CACHE_ENABLED = true;
private static final String TEMPLATE_PATH = "/WEB-INF/freemarker";
private static final String TEMPLATE_SUFFIX = ".ftl";
private static final Logger LOG = Logger.getLogger( MvcConfig.class );
#Override
public void addResourceHandlers( ResourceHandlerRegistry registry ) {
registry.addResourceHandler( "/stylesheets/**" ).addResourceLocations( "/stylesheets/" );
}
#Bean
public FreeMarkerConfigurer configureFreeMarker() {
final FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath( TEMPLATE_PATH );
return configurer;
}
#Bean
public ViewResolver configureViewResolver() {
final FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache( CACHE_ENABLED );
resolver.setSuffix( TEMPLATE_SUFFIX );
return resolver;
}
#Bean
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new DataSourceTransactionManager();
}
}
My DAO
#Component
#Transactional
public class MyDAO {
private static final Logger LOG = Logger.getLogger( MyDAO.class );
#PersistenceContext
private EntityManager entityManager;
public MyClass getMyClass() {
LOG.debug( "getMyClass()" );
final CriteriaQuery<MyClass> query = criteriaBuilder.createQuery( MyClass.class );
// more code here, but it breaks by this point
return myData;
}
}
My Updated Code
I have reached the point in which it almost all works. The EntityManager is being injected properly. However, transactions are not working. I get errors if I try to use a RESOURCE_LOCAL approach so I am looking at JTA managed transactions. When I add #Transactional on any of my DAO methods, I get a "Transaction marked for rollback" error with no further details in any log files to assist troubleshooting. If I remove the annotation from a basic read-only select, the select will work perfectly fine (not sure if I should even be putting the annotation on select-only methods). However, I obviously need this working on methods which perform db writes. If I debug through the code, it seems to retrieve the data perfectly fine. However, as it returns from the method, the javax.transaction.RollbackException gets thrown. From my understanding of everything, it seems as if the exception occurs in the AOP post-method processing.
My #Configuration class
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.example.myapp")
public class MvcConfig extends WebMvcConfigurerAdapter {
private static final boolean CACHE_ENABLED = true;
private static final String TEMPLATE_PATH = "/WEB-INF/freemarker";
private static final String TEMPLATE_SUFFIX = ".ftl";
private static final Logger LOG = Logger.getLogger( MvcConfig.class );
#Override
public void addResourceHandlers( ResourceHandlerRegistry registry ) {
registry.addResourceHandler( "/stylesheets/**" ).addResourceLocations( "/stylesheets/" );
}
#Bean
public FreeMarkerConfigurer configureFreeMarker() {
final FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath( TEMPLATE_PATH );
return configurer;
}
#Bean
public ViewResolver configureViewResolver() {
final FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache( CACHE_ENABLED );
resolver.setSuffix( TEMPLATE_SUFFIX );
return resolver;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JtaTransactionManager();
}
#Bean
public AbstractEntityManagerFactoryBean entityManagerFactoryBean() {
LocalEntityManagerFactoryBean factory = new LocalEntityManagerFactoryBean();
factory.setPersistenceUnitName( "my_db" );
return factory;
}
}
In my application I didn't implement TransactionManagerConfigurer interface. I use next code to configure JPA (with Hibernate implementation). You can do the same in your configuration class.
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean factoryBean =
new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[] {"com.dimasco.springjpa.domain"});
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
//vendorAdapter.setGenerateDdl(generateDdl)
factoryBean.setJpaVendorAdapter(vendorAdapter);
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.hbm2ddl.auto", "update");
factoryBean.setJpaProperties(additionalProperties);
return factoryBean;
}
#Bean
public DataSource dataSource() {
final ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(driverClass);
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setMinPoolSize(3);
dataSource.setMaxPoolSize(15);
dataSource.setDebugUnreturnedConnectionStackTraces(true);
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Hope this will help you)
EDIT:
You can get datasource using JNDI lookup:
#Bean
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
More details you can find in this article. There is example with JndiDatasourceConfig class.
EDIT 2:
I ahve persistence.xml in my project, but it is empty:
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="JPA_And_Spring_Test">
</persistence-unit>
</persistence>
And I didn't specify any persistent unit name in my java configuration.
The following might help, even though it uses XML-based configuration:
https://github.com/springinpractice/sip13/blob/master/helpdesk/src/main/resources/spring/beans-repo.xml
It uses Spring Data JPA, but you don't have to do that. Use
#PersistenceContext private EntityManager entityManager;
(But consider Spring Data JPA as it provides very capable DAOs out of the box.)
Side note: For DAOs, favor #Repository over #Component. Both work for component scanning, but #Repository better describes the intended use.

Resources