jOOQ with Spring Boot not inserting entity - java

I faced an issue with jOOQ not inserting entities unless the repository is annotated with #Transactional.
Here's my configuration:
#Configuration
#EnableTransactionManagement
#RequiredArgsConstructor
public class PersistenceConfig {
#Value("${spring.datasource.url}")
private String dbUrl;
#Value("${spring.datasource.username}")
private String dbUser;
#Value("${spring.datasource.password}")
private String dbPassword;
#Bean
#SneakyThrows
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
// https://mariadb.com/kb/en/about-mariadb-connector-j/
config.setDriverClassName(DatabaseDriver.MARIADB.getDriverClassName());
config.setJdbcUrl(dbUrl);
config.setUsername(dbUser);
config.setPassword(dbPassword);
config.setAutoCommit(false);
// https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
config.addDataSourceProperty("cacheServerConfiguration", true);
config.addDataSourceProperty("useServerPrepStmts", true);
config.addDataSourceProperty("useLocalSessionState", true);
config.addDataSourceProperty("cacheResultSetMetadata", true);
config.addDataSourceProperty("rewriteBatchedStatements", true);
config.addDataSourceProperty("elideSetAutoCommits", true);
config.addDataSourceProperty("maintainTimeStats", false);
config.addDataSourceProperty("cachePrepStmts", true);
config.addDataSourceProperty("prepStmtCacheSize", 350);
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
return new HikariDataSource(config);
}
#Bean
public TransactionAwareDataSourceProxy transactionAwareDataSource() {
return new TransactionAwareDataSourceProxy(dataSource());
}
#Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
#Bean
public DataSourceConnectionProvider connectionProvider() {
return new DataSourceConnectionProvider(transactionAwareDataSource());
}
#Bean
public ExceptionTranslator exceptionTransformer() {
return new ExceptionTranslator();
}
#Bean
public SpringTransactionProvider springTransactionProvider() {
return new SpringTransactionProvider(transactionManager());
}
#Bean
public DefaultConfiguration configuration() {
DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
jooqConfiguration.set(connectionProvider());
jooqConfiguration.set(new DefaultExecuteListenerProvider(exceptionTransformer()));
jooqConfiguration.set(SQLDialect.MARIADB);
jooqConfiguration.set(springTransactionProvider());
return jooqConfiguration;
}
#Bean
public DefaultDSLContext dsl() {
return new DefaultDSLContext(configuration());
}
#Bean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(transactionManager());
}
private static class ExceptionTranslator extends DefaultExecuteListener {
public void exception(ExecuteContext context) {
SQLDialect dialect = context.configuration().dialect();
SQLExceptionTranslator translator
= new SQLErrorCodeSQLExceptionTranslator(dialect.name());
context.exception(translator
.translate("Access database using jOOQ", context.sql(), context.sqlException()));
}
}
}
repository:
#Repository
public class UserRepository extends UserDao {
private final DSLContext dslContext;
public UserRepository(DSLContext dslContext) {
super(dslContext.configuration());
this.dslContext = dslContext;
}
}
So, calling userRepository.insert(...) doesn't actually insert into the database although the logs say that the following:
org.jooq.tools.LoggerListener : Executing query : insert into `user` (...)
org.jooq.tools.LoggerListener : -> with bind values : insert into `user` ...
However, If I overload UserDao's insert method and annotate it with #Transacational - it works, the rows actually get inserted. I suppose I have misconfigured something.
Spring Boot with jOOQ boot starter is used.

The problem is actually with setAutoCommit(false).

Related

multiple spring boot mongo connectors SpelEvaluationException error

I'm get this error while setting up a spring boot connecting to multiple mongo database (so custom mongoTemplate config). I'm using SpelExpression in document to dynamically determine collection name at runtime.
org.springframework.expression.spel.SpelEvaluationException: EL1057E: No bean resolver registered in the context to resolve access to bean 'prefixSupplier'
Here's my config:
#SpringBootApplication(exclude = {
MongoReactiveAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class})
public class MyApp {...}
#Configuration
public class MultipleMongoConfiguration {
#Primary
#Bean(name = "db1Properties")
#ConfigurationProperties(prefix = "mongodb.db1")
public MongoProperties getDb1Props() {
return new MongoProperties();
}
#Bean(name = "db2Properties")
#ConfigurationProperties(prefix = "mongodb.db2")
public MongoProperties getDb2Props() {
return new MongoProperties();
}
#Primary
#Bean
public ReactiveMongoDatabaseFactory db1MongoDatabaseFactory(#Qualifier("db1Properties") MongoProperties mongo) {
return new SimpleReactiveMongoDatabaseFactory(
MongoClients.create(mongo.getUri()), mongo.getDatabase()
);
}
#Bean
public ReactiveMongoDatabaseFactory db2MongoDatabaseFactory(#Qualifier("db2Properties") MongoProperties mongo) {
return new SimpleReactiveMongoDatabaseFactory(
MongoClients.create(mongo.getUri()), mongo.getDatabase()
);
}
#Primary
#Bean(name = "db1MongoTemplate")
public ReactiveMongoTemplate db1MongoTemplate() {
return new ReactiveMongoTemplate(db1MongoDatabaseFactory(getDb1Props()));
}
#Bean(name ="db2MongoTemplate")
public ReactiveMongoTemplate db2MongoTemplate() {
return new ReactiveMongoTemplate(db2MongoDatabaseFactory(getDb2Props()));
}
}
#Configuration
#EnableReactiveMongoRepositories(basePackages = {
"com.demo.database.repository.db1",
"com.demo.entity.db1"
}, reactiveMongoTemplateRef = "db1MongoTemplate")
public class Db1Configuration {
}
#Value
#Builder
#AllArgsConstructor(access = PRIVATE)
#Document(collection = "#{#prefixSupplier.get().getIdInLowerCase()}_collection")
public class Entity1 {...}
Any idea what i'm missing?
thanks
Was able to fix the issue adding this config:
#Primary
#Bean(name = "productdbMongoTemplate")
public MongoTemplate productdbMongoTemplate(ApplicationContext appContext, MongoCustomConversions customConversions) {
MongoDatabaseFactory dbFactory = productdbMongoDatabaseFactory(getProductDbProps());
MongoTemplate mongoTemplate = new MongoTemplate(dbFactory);
MongoMappingContext mappingContext = (MongoMappingContext) mongoTemplate.getConverter().getMappingContext();
mappingContext.setApplicationContext(appContext);
MappingMongoConverter mmc = (MappingMongoConverter)mongoTemplate.getConverter();
mmc.setCustomConversions(customConversions);
mmc.afterPropertiesSet();
return mongoTemplate;
}

spring-data - multiple DB: NoUniqueBeanDefinitionException No qualifying bean of type 'TransactionManager' available

I have created a spring-data project (version = 2.5.5) with two datasources GitHub. It works fine, as long, as I am using the interface CrudRepository.java. But when I am trying to implement my own DAO based on SimpleJpaRepository.java I am getting the error
No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: db1TransactionManager,db2TransactionManager
DB2Config.java (DB1Config.java is the same but instead of '2' the bean names has '1')
#Configuration
#PropertySource("classpath:db2-config.properties")
#EnableJpaRepositories(
basePackages = "com.vscoding.jpa.db2.entity",
entityManagerFactoryRef = "db2EntityManagerFactory",
transactionManagerRef = "db2TransactionManager"
)
public class DB2Config {
#Bean
#ConfigurationProperties(prefix = "db2.datasource")
public DataSource db2DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public FactoryBean<EntityManagerFactory> db2EntityManagerFactory(#Qualifier("db2DataSource") DataSource db2DataSource, Environment env){
var em = new LocalContainerEntityManagerFactoryBean();
var va = new HibernateJpaVendorAdapter();
var properties = new HashMap<String,Object>();
properties.put("hibernate.hbm2ddl.auto","create");
em.setDataSource(db2DataSource);
em.setPackagesToScan("com.vscoding.jpa.db2.entity");
em.setJpaVendorAdapter(va);
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public PlatformTransactionManager db2TransactionManager(#Qualifier("db2EntityManagerFactory") FactoryBean<EntityManagerFactory> db2EntityManagerFactory) throws Exception {
var tm = new JpaTransactionManager();
tm.setEntityManagerFactory(db2EntityManagerFactory.getObject());
return tm;
}
#Bean
#Profile("with-init")
public DataSourceInitializer dataSourceInitializer2(#Qualifier("db2DataSource") DataSource datasource) {
var populator = new ResourceDatabasePopulator();
populator.addScript(new ClassPathResource("db2.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(datasource);
dataSourceInitializer.setDatabasePopulator(populator);
return dataSourceInitializer;
}
}
ProductCustomRepository.java
#Repository
#Transactional(transactionManager = "db2TransactionManager")
public class ProductCustomRepository extends SimpleJpaRepository<ProductEntity2, Integer> {
private final EntityManager entityManager;
public ProductCustomRepository(#Qualifier("db2EntityManagerFactory") EntityManager entityManager) {
super(ProductEntity2.class, entityManager);
this.entityManager = entityManager;
}
public List<ProductEntity2> customQuery() {
var query = entityManager.createQuery("SELECT p FROM ProductEntity2 p WHERE p.name='special'",ProductEntity2.class);
return query.getResultList();
}
}
I would expect, that #Transactional(transactionManager = "db2TransactionManager") would select the right transactionManager, but maybe I am missing something.
ProductCustomRepositoryTest.java (test to reproduce the error)
#SpringBootTest
class ProductCustomRepositoryTest {
#Autowired
private ProductCustomRepository sut;
#Test
void customQuery() {
//Given
var special = new ProductEntity2("special");
sut.save(special);
//When
var result = sut.customQuery();
//Then
assertTrue(result.contains(special));
}
}
Thanks in advance.
As mentioned by Simon, I could solve the error, by following https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
Changed my ProductCustomRepository.javato CustomRepositoryImpl.java and added Interface CustomRepository.java
#Repository
#Transactional(transactionManager = "db2TransactionManager")
public class CustomRepositoryImpl implements CustomRepository {
// no changes here
}
Then extend my main Repository with the interface.
public interface ProductRepository2 extends CrudRepository<ProductEntity2,Integer>,CustomRepository {
}

TaskConfigurer does not considering schema name for datasource

I have 2 data source. For one data source, I want to use custom schema name. For this reason, I am setting my data source url like
spring.datasource.url=jdbc:postgresql://192.168.33.10/analytics?currentSchema=bahmni_mart_scdf.
But it's creating all the tables in public schema, instead of bahmni_mart_scdf schema.
I already override TaskRepositoryInitializer bean and implements TaskConfigurer.
Here are my implementations
DatabaseConfiguration.class
#Configuration
public class DatabaseConfiguration {
#Bean(name = "martDb")
#ConfigurationProperties(prefix = "spring.ds_mart")
public DataSource martDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "martJdbcTemplate")
public JdbcTemplate martJdbcTemplate(#Qualifier("martDb") DataSource dsMart) {
return new JdbcTemplate(dsMart);
}
#Bean(name = "scdfDb")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "scdfJdbcTemplate")
public JdbcTemplate scdfJdbcTemplate(#Qualifier("scdfDb") DataSource scdfDb) {
return new JdbcTemplate(scdfDb);
}
}
DataloadTaskConfigurer.class
#Component
public class DataloadTaskConfigurer implements TaskConfigurer {
private DataSource dataSource;
private TaskRepository taskRepository;
private TaskExplorer taskExplorer;
private PlatformTransactionManager transactionManager;
#Autowired
public DataloadTaskConfigurer(#Qualifier("scdfJdbcTemplate") JdbcTemplate jdbcTemplate) {
this.dataSource = jdbcTemplate.getDataSource();
TaskExecutionDaoFactoryBean taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(dataSource);
this.taskRepository = new SimpleTaskRepository(taskExecutionDaoFactoryBean);
this.taskExplorer = new SimpleTaskExplorer(taskExecutionDaoFactoryBean);
}
#Override
public TaskRepository getTaskRepository() {
return this.taskRepository;
}
#Override
public PlatformTransactionManager getTransactionManager() {
if (this.transactionManager == null) {
this.transactionManager = new DataSourceTransactionManager(this.dataSource);
}
return this.transactionManager;
}
#Override
public TaskExplorer getTaskExplorer() {
return this.taskExplorer;
}
public DataSource getTaskDataSource() {
return this.dataSource;
}
}
TaskConfiguration.class
#Configuration
public class TaskConfiguration {
#Bean
public TaskRepositoryInitializer taskRepositoryInitializerInDataMart(#Qualifier("scdfJdbcTemplate") JdbcTemplate jdbcTemplate) throws Exception {
TaskRepositoryInitializer taskRepositoryInitializer = new TaskRepositoryInitializer();
taskRepositoryInitializer.setDataSource(jdbcTemplate.getDataSource());
taskRepositoryInitializer.afterPropertiesSet();
return taskRepositoryInitializer;
}
}
application.properties
spring.datasource.url=jdbc:postgresql://192.168.33.10/analytics?currentSchema=bahmni_mart_scdf
spring.datasource.username=analytics
spring.datasource.password=""
spring.datasource.driver-class-name=org.postgresql.Driver
spring.ds_mart.url=jdbc:postgresql://192.168.33.10/analytics
spring.ds_mart.username=analytics
spring.ds_mart.password=""
spring.ds_mart.driver-class-name=org.postgresql.Driver
You'll need to not override the TaskRepositoryInitializer, but turn it off all together via spring.cloud.task.initialize.enable=false. From there, using Spring Boot, you'll want to pass in the schema for each datasource:
spring.datasource.schema=schema1.sql
spring.ds_mart.schema=schema2.sql
The problem was with the postgresql driver version. Once I update to latest version (42.2.2) everything working fine as expected

Dynamic datasource routing - DataSource router not initialized

I'm referring to this article, in which we can use the AbstractRoutingDataSource from Spring Framework to dynamically change the data source used by the application. I'm using Mybatis (3.3.0) with Spring (4.1.6.RELEASE). I want to switch to the backup database if exception occurs while getting data from main db. In this example, i have used hsql and mysql db.
RoutingDataSource:
public class RoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getTargetDataSource();
}
}
DataSourceContextHolder:
public class DataSourceContextHolder {
private static final ThreadLocal<DataSourceEnum> contextHolder = new ThreadLocal<DataSourceEnum>();
public static void setTargetDataSource(DataSourceEnum targetDataSource) {
contextHolder.set(targetDataSource);
}
public static DataSourceEnum getTargetDataSource() {
return (DataSourceEnum) contextHolder.get();
}
public static void resetDefaultDataSource() {
contextHolder.remove();
}
}
ApplicationDataConfig:
#Configuration
#MapperScan(basePackages = "com.sample.mapper")
#ComponentScan("com.sample.config")
#PropertySource(value = {"classpath:app.properties"},
ignoreResourceNotFound = true)
public class ApplicationDataConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer =
new PropertySourcesPlaceholderConfigurer();
return configurer;
}
#Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setDefaultTargetDataSource(dataSource1());
Map<Object, Object> targetDataSource = new HashMap<Object, Object>();
targetDataSource.put(DataSourceEnum.HSQL, dataSource1());
targetDataSource.put(DataSourceEnum.BACKUP, dataSource2());
routingDataSource.setTargetDataSources(targetDataSource);
sessionFactory.setDataSource(routingDataSource);
sessionFactory.setTypeAliasesPackage("com.sample.common.domain");
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath*:com/sample/mapper/**/*.xml"));
return sessionFactory;
}
#Bean
public DataSource dataSource1() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript(
"classpath:database/app-hsqldb-schema.sql").addScript(
"classpath:database/app-hsqldb-datascript.sql").build();
}
#Bean
public DataSource dataSource2() {
PooledDataSourceFactory pooledDataSourceFactory = new PooledDataSourceFactory();
pooledDataSourceFactory.setProperties(jdbcProperties());
return pooledDataSourceFactory.getDataSource();
}
#Bean
protected Properties jdbcProperties() {
//Get the data from properties file
Properties jdbcProperties = new Properties();
jdbcProperties.setProperty("url", datasourceUrl);
jdbcProperties.setProperty("driver", datasourceDriver);
jdbcProperties.setProperty("username", datasourceUsername);
jdbcProperties.setProperty("password", datasourcePassword);
jdbcProperties.setProperty("poolMaximumIdleConnections", maxConnectionPoolSize);
jdbcProperties.setProperty("poolMaximumActiveConnections", minConnectionPoolSize);
return jdbcProperties;
}
}
Client:
#Autowired
private ApplicationMapper appMapper;
public MyObject getObjectById(String Id) {
MyObject myObj = null;
try{
DataSourceContextHolder.setTargetDataSource(DataSourceEnum.HSQL);
myObj = appMapper.getObjectById(Id);
}catch(Throwable e){
DataSourceContextHolder.setTargetDataSource(DataSourceEnum.BACKUP);
myObj = appMapper.getObjectById(Id);
}finally{
DataSourceContextHolder.resetDefaultDataSource();
}
return getObjectDetails(myObj);
}
I'm getting the following exception
### Error querying database. Cause: java.lang.IllegalArgumentException: DataSource router not initialized
However i'm able to get things working if i'm using only one db at a time, this means there is no issue with data source configuration.
Can you try this last line once (in same order) :-
targetDataSource.put(DataSourceEnum.HSQL, dataSource1());
targetDataSource.put(DataSourceEnum.BACKUP, dataSource2());
routingDataSource.setTargetDataSources(targetDataSource);
routingDataSource.afterPropertiesSet();
I got the same issue and found a solution using the SchemaExport class of hibernate.
For each DataSourceEnum you can manually initialize the datasource.
here is my detailed answer to my own issue discription

Using EhCache in Spring 4 without XML

Is there a way to initialize EhCache without xml in either Spring 4 or with Spring Boot?
I noticed Spring Boot 1.0.0.RC3 doesn't have any ehcache dependencies but the Spring 4.0GA release post mentioned it has improved support for EhCache. Also, Spring 3 had the class org.springframework.cache.ehcache.EhCacheCacheManager but that's no longer part of the dependencies.
Edit:
Spring 4 does have EhCache support. You must add the dependency:
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
Edit2:
I've tried the following and I think I'm close but I'm getting an error:
#Bean
#Override
public CacheManager cacheManager() {
CacheConfiguration cacheConfiguration = new CacheConfiguration();
cacheConfiguration.setName("primary");
cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
cacheConfiguration.setMaxEntriesLocalHeap(0);
net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
config.addCache(cacheConfiguration);
net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager(config);
cacheManager.setName("EhCache");
return new EhCacheCacheManager(cacheManager);
}
#Bean
public EhCacheManagerFactoryBean factoryBean() {
return new EhCacheManagerFactoryBean();
}
Error
Caused by: net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: [Programmatically configured]
at net.sf.ehcache.CacheManager.assertNoCacheManagerExistsWithSameName(CacheManager.java:590)
at net.sf.ehcache.CacheManager.init(CacheManager.java:384)
at net.sf.ehcache.CacheManager.<init>(CacheManager.java:263)
at org.springframework.cache.ehcache.EhCacheManagerFactoryBean.afterPropertiesSet(EhCacheManagerFactoryBean.java:166)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)
... 15 more
XML-less configuration of EhCache in Spring
#Configuration
#EnableCaching
public class CachingConfig implements CachingConfigurer {
#Bean(destroyMethod="shutdown")
public net.sf.ehcache.CacheManager ehCacheManager() {
CacheConfiguration cacheConfiguration = new CacheConfiguration();
cacheConfiguration.setName("myCacheName");
cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
cacheConfiguration.setMaxEntriesLocalHeap(1000);
net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
config.addCache(cacheConfiguration);
return net.sf.ehcache.CacheManager.newInstance(config);
}
#Bean
#Override
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheManager());
}
#Bean
#Override
public KeyGenerator keyGenerator() {
return new SimpleKeyGenerator();
}
#Bean
#Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver();
}
#Bean
#Override
public CacheErrorHandler errorHandler() {
return new SimpleCacheErrorHandler();
}
}
Alternatively for testing, you can use a simple ConcurrentMapCache running without XML as below.
#Configuration
#EnableCaching
public class CachingConfig implements CachingConfigurer {
#Bean
#Override
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
List<Cache> caches = new ArrayList<Cache>();
caches.add(new ConcurrentMapCache("myCacheName"));
cacheManager.setCaches(caches);
return cacheManager;
}
#Bean
#Override
public KeyGenerator keyGenerator() {
return new SimpleKeyGenerator();
}
#Bean
#Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver();
}
#Bean
#Override
public CacheErrorHandler errorHandler() {
return new SimpleCacheErrorHandler();
}
}
Edit: Updated to add shutdown method on underlying cache
Edit: Added configuration for error handler and cache resolver
Edit: changing constructor call on SimpleCacheResolver which resolves CacheManager must not be null issue (Spring v5.1+)
#Configuration
#EnableCaching
public class CachingConfig implements CachingConfigurer {
public static final String USER_CACHE_INSTANCE = "my-spring-ehcache";
private final CacheManager cacheManager;
private final net.sf.ehcache.CacheManager ehCacheManager;
public CachingConfig() {
CacheConfiguration cacheConfiguration = new CacheConfiguration();
cacheConfiguration.setName(USER_CACHE_INSTANCE);
cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
cacheConfiguration.setMaxEntriesLocalHeap(1000);
net.sf.ehcache.config.Configuration config
= new net.sf.ehcache.config.Configuration();
config.addCache(cacheConfiguration);
this.ehCacheManager = net.sf.ehcache.CacheManager.newInstance(config);
this.cacheManager = new EhCacheCacheManager(ehCacheManager);
}
#Bean(destroyMethod="shutdown")
public net.sf.ehcache.CacheManager ehCacheManager() {
return ehCacheManager;
}
#Bean
#Override
public CacheManager cacheManager() {
return cacheManager;
}
#Bean
#Override
public KeyGenerator keyGenerator() {
return new SimpleKeyGenerator();
}
#Bean
#Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager);
}
#Bean
#Override
public CacheErrorHandler errorHandler() {
return new SimpleCacheErrorHandler();
}
}
I do this at two levels of abstraction, a configuration file per technology (Ehcache, Redis, etc.) and a general configuration file.
Here's the one for Ehcache (Redis would be similar):
#Configuration
public class EhCacheConfiguration {
#Bean
public EhCacheCacheManager ehCacheCacheManager() {
return new EhCacheCacheManager(ehCacheManagerFactoryBean().getObject());
}
#Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
cacheManagerFactoryBean.setShared(true);
return cacheManagerFactoryBean;
}
}
And here's the general one (complete with Redis hooks):
#Configuration
#EnableCaching
public class CachingConfiguration implements CachingConfigurer {
#Qualifier("ehCacheCacheManager")
#Autowired(required = false)
private CacheManager ehCacheCacheManager;
#Qualifier("redisCacheManager")
#Autowired(required = false)
private CacheManager redisCacheManager;
#Bean
#Override
public CacheManager cacheManager() {
List<CacheManager> cacheManagers = Lists.newArrayList();
if (this.ehCacheCacheManager != null) {
cacheManagers.add(this.ehCacheCacheManager);
}
if (this.redisCacheManager != null) {
cacheManagers.add(this.redisCacheManager);
}
CompositeCacheManager cacheManager = new CompositeCacheManager();
cacheManager.setCacheManagers(cacheManagers);
cacheManager.setFallbackToNoOpCache(false);
return cacheManager;
}
#Bean
#Override
public KeyGenerator keyGenerator() {
return new DefaultKeyGenerator();
}
}

Categories