I have some tests in error after upgrading from Spring boot 2.5.6 to 2.7.3. With this upgrade, h2 version has been upgraded from 1.4.200 to 2.1.214. In the pom, the version of spring-test-dbunit used is 1.3.0. The version of dbunit is 2.7.3.
For information we use Oracle for the database and h2 for tests.
Our application works on two different schema.
I have some entities like this:
#Entity
#Table(name = "TABLEONE")
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
#Immutable
public class TableOne implements Serializable {
...
}
#Entity
#Table(schema = "OTHERSCHEMA", name = "TABLETWO_C")
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class TableTwo {
...
}
#Entity
#Table(schema = "OTHERSCHEMA", name = "TABLETHREE")
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
#Immutable
public class TableThree {
...
#ManyToOne
#JoinColumn(name = "ID_TABLEONE")
private TableOne tableOne;
....
}
In the application.yml for tests, we have this (for "spring.datasource.url"):
url: jdbc:h2:mem:my-app;DB_CLOSE_DELAY=-1;MODE=Oracle
For the tests, we have one configuration for dbunit like this:
#TestConfiguration
public class DbUnitTestConfiguration {
#Autowired
private DataSource dataSource;
public DatabaseConfigBean getDatabaseConfigBean(boolean qualifiedTableNames) {
final DatabaseConfigBean databaseConfigBean = new DatabaseConfigBean();
databaseConfigBean.setDatatypeFactory(new H2DataTypeFactory());
databaseConfigBean.setQualifiedTableNames(qualifiedTableNames);
return databaseConfigBean;
}
#Bean(name = "dbUnitDatabaseConnection")
public DatabaseDataSourceConnectionFactoryBean getDatabaseDataSourceConnectionFactoryBean() {
final DatabaseDataSourceConnectionFactoryBean databaseConfigBean = new DatabaseDataSourceConnectionFactoryBean();
databaseConfigBean.setDatabaseConfig(getDatabaseConfigBean(false));
databaseConfigBean.setDataSource(dataSource);
return databaseConfigBean;
}
#Bean(name = "otherSchemaDatabaseConnection")
public DatabaseDataSourceConnectionFactoryBean getOtherSchemaDatabaseDataSourceConnectionFactoryBean() {
final DatabaseDataSourceConnectionFactoryBean databaseConfigBean = new DatabaseDataSourceConnectionFactoryBean();
databaseConfigBean.setDatabaseConfig(getDatabaseConfigBean(true));
databaseConfigBean.setDataSource(dataSource);
databaseConfigBean.setSchema("OTHERSCHEMA");
return databaseConfigBean;
}
}
The tests are like this:
#ExtendWith(SpringExtension.class)
#SpringBootTest(properties = { "spring.mvc.pathmatch.matching-strategy=ant-path-matcher" })
#AutoConfigureMockMvc
#ContextConfiguration(classes = { SecurityTestConfiguration.class, SpecificWebMvcTestConfiguration.class,
DbUnitTestConfiguration.class })
#TestExecutionListeners(listeners = DbUnitTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
#DbUnitConfiguration(databaseConnection = { "dbUnitDatabaseConnection", "otherSchemaDatabaseConnection" })
#WithMockUser
#DatabaseSetup(connection = "otherSchemaDatabaseConnection", value = { "classpath:dataSet/tableTwo.xml" })
#DatabaseSetup(value = { "classpath:dataSet/tableOne.xml" })
#DatabaseTearDown(value = { "classpath:dataSet/tableOne.xml" }, type = DatabaseOperation.DELETE_ALL)
#DatabaseTearDown(connection = "otherSchemaDatabaseConnection", value = {
"classpath:dataSet/tableTwo.xml" }, type = DatabaseOperation.DELETE_ALL)
public class MyTest {
#Autowired
private MockMvc mvc;
#Test
public void myTest() throws Exception {
...
}
}
I have errors like this:
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "TABLEONE" not found; SQL statement:
alter table otherschema.tablethree add constraint FKexerkn1ptnbr1k56036qd0snn foreign key (id_tableone) references tableone [42102-214]
and
org.dbunit.dataset.NoSuchTableException: OTHERSCHEMA.TABLETWO_C
I also see that log:
org.dbunit.database.DatabaseDataSet : Table 'OTHERSCHEMA.TABLETWO_C' not found in tableMap=org.dbunit.dataset.OrderedTableNameMap[_tableNames=[OTHERSCHEMA.TABLETEN, OTHERSCHEMA.TABLEELEVEN...], _tableMap={OTHERSCHEMA.TABLETEN=null, OTHERSCHEMA.TABLEELEVEN=null...}, _caseSensitiveTableNames=false]
It seems that the configuration that was done before and working with previous version of h2 does not work with new version of h2. I saw a solution by setting qualified table names to true and it is what is done for the other schema so I do not understand.
For the first error, the alter table works on a table of otherschema and did not find tableone that is in the default schema (dbUnitDatabaseConnection connection) so it seems to be a problem between dbUnitDatabaseConnection
and otherSchemaDatabaseConnection no?
For the second error, I do not understand why the table does not exist in the list of tables with OTHERSCHEMA indicated in the log.
Can you help me please?
Related
I created a service that connects to two schemas (ex. fo_pgdb, if_pgdb) My issue is when the service queries the table in the if_pgdb schema it looks as though it is querying the table in the fo_pgdb schema. I have checked and hard coded the database URLs in both class attributes (shown in code examples below) look fine. What could be the issue?
example:
query on table in fo_pgdb schema is "select * from bid_lines where bidlinseqnumber in (123, 345) returns a result set. because ids 123 and 345 have records in the table.
query on table in if_pgdb schema is "select * from bid_lines where bidlinseqnumber in (567, 8910) returns empty result set. But ids 567 and 8910 those records with those ids are in the table.
test: when I use the ids 123 and 345 in the query on the table in the if_pgdb schema I get the same records that are in the table that are in the fo_pgdb table. That should not happen.
#Configuration
#EnableR2dbcRepositories(entityOperationsRef = "foEntityTemplate", basePackages = "com.r2dbc.poc.repository")
public class FODatabaseConfig {
//#Value("${spring.r2dbc.fo.connection.url}")
private String url = "r2dbc:postgresql://username:password#database-dev-fo-css-rr-db.corp.com:1200/fo_pgdb";
#Bean
#Qualifier("foConnectionFactory")
public ConnectionFactory foConnectionFactory() {
return ConnectionFactories.get(url);
}
#Bean
public R2dbcEntityOperations foEntityTemplate(#Qualifier("foConnectionFactory") ConnectionFactory connectionFactory) {
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
DatabaseClient databaseClient = DatabaseClient.builder()
.connectionFactory(connectionFactory)
.bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory())
.build();
return new R2dbcEntityTemplate(databaseClient, strategy);
}
}
#Configuration
#EnableR2dbcRepositories(entityOperationsRef = "ifEntityTemplate")
public class IFDatabaseConfig {
//#Value("${spring.r2dbc.if.connection.url}")
private String url = "r2dbc:postgresql://username:password#database-blue-if-CSS-db.corp.com:1200/if_pgdb";
#Bean
#Qualifier("ifConnectionFactory")
public ConnectionFactory ifConnectionFactory() {
return ConnectionFactories.get(url);
}
#Bean
public R2dbcEntityOperations ifEntityTemplate(#Qualifier("ifConnectionFactory") ConnectionFactory connectionFactory) {
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
DatabaseClient databaseClient = DatabaseClient.builder()
.connectionFactory(connectionFactory)
.bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory())
.build();
return new R2dbcEntityTemplate(databaseClient, strategy);
}
}
#Service
#RequiredArgsConstructor
public class CrewMemberSchedulePeriodPaymentService {
private final FOCrewMemberBidLineRepository foCrewMemberBidlineRepository;
private final IFCrewMemberBidLineRepository ifCrewMemberBidLineRepository;
public Flux<FOCrewMemberBidLine> getFOBidLines(List<Long> id) {
return foCrewMemberBidlineRepository.findAllById(id);
}
public Flux<IFCrewMemberBidLine> getIFBidLines(List<Long> id) {
return ifCrewMemberBidLineRepository.findAllById(id);
}
}
#Repository
public interface FOCrewMemberBidLineRepository extends R2dbcRepository<FOCrewMemberBidLine, Long> {
#Override
Flux<FOCrewMemberBidLine> findAllById(Iterable<Long> longs);
}
#Repository
public interface IFCrewMemberBidLineRepository extends R2dbcRepository<IFCrewMemberBidLine, Long> {
#Override
Flux<IFCrewMemberBidLine> findAllById(Iterable<Long> longs);
}
#Table(value = "BID_LINES")
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Data
public class FOCrewMemberBidLine {
#Id
#Column(value = "bidlinseqnumber")
private Long bidlinseqnumber;
#Column(value = "bidlinschedperiod")
private String bidlinschedperiod;
}
#Table(value = "BID_LINES")
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Data
public class IFCrewMemberBidLine {
#Id
#Column(value = "bidlinseqnumber")
private Long bidlinseqnumber;
#Column(value = "bidlinschedperiod")
private String bidlinschedperiod;
}
Maybe be you can add the connection factory invkoing the method, like this:
#Bean
#Qualifier("foConnectionFactory")
public ConnectionFactory foConnectionFactory() {
return ConnectionFactories.get("r2dbc:postgresql://username:password#database-dev-fo-css-rr-db.corp.com:1200/fo_pgdb");
}
#Bean
#Qualifier("ifConnectionFactory")
public ConnectionFactory ifConnectionFactory() {
return ConnectionFactories.get("r2dbc:postgresql://username:password#database-blue-if-CSS-db.corp.com:1200/if_pgdb");
}
#Bean
public R2dbcEntityOperations ifEntityTemplate() {
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
DatabaseClient databaseClient = DatabaseClient.builder()
.connectionFactory(ifConnectionFactory()) //<-- change
.bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory())
.build();
return new R2dbcEntityTemplate(databaseClient, strategy);
}
#Bean
public R2dbcEntityOperations foEntityTemplate() {
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
DatabaseClient databaseClient = DatabaseClient.builder()
.connectionFactory(foConnectionFactory()) //<-- change
.bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory())
.build();
return new R2dbcEntityTemplate(databaseClient, strategy);
}
You can have all of your beans in the same class and each bean will be created with the connection factory that you need.
Cheers.
How can I persist an entity that contains a #Formula field in it to H2 database?
I encounter the following exception:
...
Caused by: org.h2.jdbc.JdbcSQLException: Function "UNIX_TIMESTAMP" not found;
...
Post class:
#Entity
public class Post {
// ...
#Formula("UNIX_TIMESTAMP(creation_date_time) / 24 * 60 * 60 * 1000 + likes_count")
private long score;
// ...
}
PostRepositoryTest class:
#ExtendWith(SpringExtension.class)
#DataJpaTest
class PostRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private PostRepository postRepository;
#Test
void savePost() {
entityManager.persist(new Post());
List<Post> posts = postRepository.findAll();
assertEquals(1, posts.size());
}
}
According to https://h2database.com/html/features.html#user_defined_functions, you could define such a function in src/test/resources/import.sql:
CREATE ALIAS IF NOT EXISTS UNIX_TIMESTAMP FOR "acme.H2Function.unixTimestamp";
and define the this class:
package acme;
public class H2Function {
public static long unixTimestamp(java.sql.Timestamp timestamp) {
return timestamp.getTime() / 1000L;
}
}
Spring JPA will auto call import.sql when you start you unit test.
NOTE: Must naming import.sql, not schema.sql, not data.sql
I am new to ignite , I am trying to fetch data using ignite repository but below query returns 'null'.
my repository
#Component
#RepositoryConfig(cacheName = "UserCache")
#Repository
public interface UserRepository extends IgniteRepository<UserEntity, Long> {
#Query("select a.* from UserEntity a where a.lastname=? ")
UserEntity selectUserlastName(String plastName);
My cache configuration as
CacheConfiguration<Long, UserEntity> lUserCacheConfig =
createCacheConfigurationStore("UserCache", UserCacheStore.class);
CacheJdbcPojoStoreFactory<Long, UserEntity> lUserJdbcStoreFactory = new
CacheJdbcPojoStoreFactory<>();
UserJdbcPojoStoreFactory<? super Long, ? super UserEntity>
lUserJdbcPojoStoreFactory = new UserJdbcPojoStoreFactory<>();
lUserJdbcStoreFactory.setDataSource(datasource);
lUserJdbcStoreFactory.setDialect(new OracleDialect());
lUserJdbcStoreFactory.setTypes(lUserJdbcPojoStoreFactory.
configJdbcContactType());
lUserCacheConfig.setCacheStoreFactory(lUserJdbcStoreFactory);
// Configure Cache..
cfg.setCacheConfiguration(lUserCacheConfig);
My PojoStore is as below:
public class UserJdbcPojoStoreFactory<K, V> extends
AnstractJdbcPojoStoreFactory<Long, UserEntity> {
private static final long serialVersionUID = 1L;
#Autowired
DataSource datasource;
#Override
public CacheJdbcPojoStore<Long, UserEntity> create() {
// TODO Auto-generated method stub
setDataSource(datasource);
return super.create();
}
#Override
public JdbcType configJdbcContactType() {
JdbcType jdbcContactType = new JdbcType();
jdbcContactType.setCacheName("UserCache");
jdbcContactType.setKeyType(Long.class);
jdbcContactType.setValueType(UserEntity.class);
jdbcContactType.setDatabaseTable("USER");
jdbcContactType.setDatabaseSchema("ORGNITATION");
jdbcContactType.setKeyFields(new JdbcTypeField(Types.INTEGER, "id",
Long.class, "id"));
jdbcContactType.setValueFields(
new JdbcTypeField(Types.VARCHAR, "NAME", String.class, "NAME"), //
new JdbcTypeField(Types.VARCHAR, "LASTNAME", String.class, "lastname"),
//
return jdbcContactType;
}
}
Please suggest ..
Please check that #Query annotation imported from ignite-spring-data library and test your query using SqlFieldsQuery.
I have a problem with injection spring beans into some ignite's classes. I'm trying to create this: Client -> Apache Ignite -> Spring-Data -> DataBase
Maybe it is wrong, i'm not sure.
So, at this moment my classes look like:
AppConfiguration
#Configuration
#ComponentScan(basePackages = arrayOf("com.ignite.cache"))
open class AppConfiguration : Serializable {
private val logger: Logger = Logger.getLogger(AppConfiguration::class.java)
#Bean
open fun igniteInstance(): Ignite {
val cfg = IgniteConfiguration()
cfg.igniteInstanceName = "springDataNode"
cfg.isPeerClassLoadingEnabled = true
var clientCache: CacheConfiguration<Long, Client> = CacheConfiguration("ClientCache")
clientCache.apply {
setIndexedTypes(Long::class.java, Client::class.java)
setCacheStoreFactory(FactoryBuilder.factoryOf(ClientStore::class.java))
isReadThrough = true
isWriteThrough = true
}
cfg.setCacheConfiguration(clientCache)
return Ignition.start(cfg)
}
}
DataSourceConfiguration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = arrayOf("com.ignite.cache.model.repositories.springdatarepository"))
#EnableIgniteRepositories(basePackages = arrayOf("com.ignite.cache.model.repositories.igniterepository"))
#ComponentScan(basePackages = arrayOf("com.ignite.cache.model"))
open class DataSourceConfiguration : Serializable {
#Bean
open fun entityManagerFactory(): LocalContainerEntityManagerFactoryBean {
var entityManagerFactory: LocalContainerEntityManagerFactoryBean = LocalContainerEntityManagerFactoryBean()
entityManagerFactory.apply {
dataSource = dataSource()
setPackagesToScan("com.ignite.cache.model")
var vendorAdapter: HibernateJpaVendorAdapter = HibernateJpaVendorAdapter()
vendorAdapter.apply {
setGenerateDdl(true)
setShowSql(true)
}
var properties: Properties = Properties()
properties.apply {
put("database.dialet", "org.hibernate.dialect.PostgreSQL95Dialect")
put("database.globally_quoted_identifiers", "false")
put("database.enable_lazy_load_no_trans", "true")
put("database.show_sql", "true")
}
jpaVendorAdapter = vendorAdapter
setJpaProperties(properties)
}
return entityManagerFactory
}
#Bean
open fun dataSource(): DataSource {
var source: ComboPooledDataSource = ComboPooledDataSource()
source.apply {
driverClass = "org.postgresql.Driver"
jdbcUrl = "jdbc:postgresql://localhost:5432/ignite"
user = "postgres"
password = "1111"
acquireIncrement = 5
idleConnectionTestPeriod = 60
maxPoolSize = 20
minPoolSize = 10
initialPoolSize = 10
}
return source
}
#Bean
open fun transactionManager() : PlatformTransactionManager {
var manager: JpaTransactionManager = JpaTransactionManager()
manager.apply {
entityManagerFactory = entityManagerFactory().nativeEntityManagerFactory
}
return manager
}
#Bean
open fun exceptionTranslator(): PersistenceExceptionTranslationPostProcessor = PersistenceExceptionTranslationPostProcessor()
}
Entity:
#Entity
#Table(name = "client")
data class Client
(
#Id
#Column(name = "id")
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
var id: Long = 0,
#Column(name = "email", nullable = false, unique = true)
var email: String = "",
#Column(name = "login", nullable = false, unique = true)
var login: String = ""
) : Serializable
Repositories:
#RepositoryConfig(cacheName = "ClientCache")
interface IgniteClientRepository : IgniteRepository<Client, Long> {
}
#Repository
interface ClientRepository : CrudRepository<Client, Long>
Standart implemenation for service classes and CacheStore for ignite:
public class ClientStore implements CacheStore<Long, Client>, Serializable {
private Logger logger = Logger.getLogger(ClientStore.class);
#SpringResource
private IClientService clientRepository; // <- error is here (NPE)
#Override
public void loadCache(IgniteBiInClosure<Long, Client> igniteBiInClosure, #Nullable Object... objects) throws CacheLoaderException {
Iterable<Client> clients = clientRepository.findAll();
for(Client client : clients) {
igniteBiInClosure.apply(client.getId(), client);
}
}
...
}
Main:
public class Main {
private static Logger logger = Logger.getLogger(Main.class);
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfiguration.class, DataSourceConfiguration.class);
Ignite ignite = context.getBean(Ignite.class);
(ignite.getOrCreateCache("ClientCache")).loadCache(null);
IgniteClientRepository clientRepository = context.getBean(IgniteClientRepository.class);
Iterable<Client> clients = clientRepository.findAll();
for(Client client : clients) {
logger.info(client);
}
logger.info("Finish");
}
}
But when i try to load some data from database into cache i get error NPE:
Exception in thread "main" javax.cache.integration.CacheLoaderException: java.lang.NullPointerException
at org.apache.ignite.internal.processors.cache.store.GridCacheStoreManagerAdapter.loadCache(GridCacheStoreManagerAdapter.java:528)
at org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter.localLoadCache(GridDhtCacheAdapter.java:486)
at org.apache.ignite.internal.processors.cache.GridCacheProxyImpl.localLoadCache(GridCacheProxyImpl.java:217)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$LoadCacheJob.localExecute(GridCacheAdapter.java:5439)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$LoadCacheJobV2.localExecute(GridCacheAdapter.java:5488)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter$TopologyVersionAwareJob.execute(GridCacheAdapter.java:6103)
at org.apache.ignite.compute.ComputeJobAdapter.call(ComputeJobAdapter.java:132)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor$C2.execute(GridClosureProcessor.java:1842)
at org.apache.ignite.internal.processors.job.GridJobWorker$2.call(GridJobWorker.java:566)
at org.apache.ignite.internal.util.IgniteUtils.wrapThreadLoader(IgniteUtils.java:6621)
at org.apache.ignite.internal.processors.job.GridJobWorker.execute0(GridJobWorker.java:560)
at org.apache.ignite.internal.processors.job.GridJobWorker.body(GridJobWorker.java:489)
at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:110)
at org.apache.ignite.internal.processors.job.GridJobProcessor.processJobExecuteRequest(GridJobProcessor.java:1114)
at org.apache.ignite.internal.processors.task.GridTaskWorker.sendRequest(GridTaskWorker.java:1379)
at org.apache.ignite.internal.processors.task.GridTaskWorker.processMappedJobs(GridTaskWorker.java:640)
at org.apache.ignite.internal.processors.task.GridTaskWorker.body(GridTaskWorker.java:532)
at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:110)
at org.apache.ignite.internal.processors.task.GridTaskProcessor.startTask(GridTaskProcessor.java:743)
at org.apache.ignite.internal.processors.task.GridTaskProcessor.execute(GridTaskProcessor.java:443)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:447)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:418)
at org.apache.ignite.internal.processors.closure.GridClosureProcessor.callAsync(GridClosureProcessor.java:402)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter.globalLoadCacheAsync(GridCacheAdapter.java:3681)
at org.apache.ignite.internal.processors.cache.GridCacheAdapter.globalLoadCache(GridCacheAdapter.java:3657)
at org.apache.ignite.internal.processors.cache.IgniteCacheProxy.loadCache(IgniteCacheProxy.java:387)
at com.ignite.cache.Main.main(Main.java:22)
Caused by: java.lang.NullPointerException
at com.ignite.cache.model.service.ClientStore.loadCache(ClientStore.java:30)
at org.apache.ignite.internal.processors.cache.store.GridCacheStoreManagerAdapter.loadCache(GridCacheStoreManagerAdapter.java:502)
... 26 more
I still can't figure out why my client service doesn't inject into CacheStore class. Maybe i should use xml config instead of java-class config for ignite?
When you start Ignite using Ignition#start with IgniteConfiguration object, it's not aware of Spring context at all. You need to use IgniteSpring#start methods instead to provide context explicitly. Another option is to utilize IgniteSpringBean that already implements ApplicationContextAware and starts Ignite instance properly.
Also note that you will need to provide either bean name or bean class as a parameter to #SpringResource annotation.
I don't know Ignite and I don't know the programming language you are using but it looks like Ignite and not Spring is creating your ClientStore instances. Therefore you need to inject the dependencies manually.
I'm not really familiar with the FactoryBuilder used there, so there might be nice solutions if you can use a constructor with arguments or even a lambda, but if that doesn' work, you can store a reference to the repository in static field and get it from there in the constructor of the ClientStore.
It'm using a spring boot application with cache enabled.
Environment (pom.xml):
Spring:
org.springframework.boot:spring-boot-starter-amqp:jar:1.3.3.RELEASE
org.springframework:spring-messaging:jar:4.2.5.RELEASE
org.springframework.amqp:spring-rabbit:jar:1.5.4.RELEASE
org.springframework.retry:spring-retry:jar:1.1.2.RELEASE
org.springframework:spring-core:jar:4.2.5.RELEASE:compile
org.springframework.cloud:spring-cloud-aws-context:jar:1.0.4.RELEASE
org.springframework:spring-context:jar:4.2.5.RELEASE
org.springframework.data:spring-data-jpa:jar:1.9.4.RELEASE
org.springframework:spring-context-support:jar:4.2.5.RELEASE
Hibernate
org.hibernate:hibernate-validator:jar:5.2.2.Final
org.hibernate.javax.persistence:hibernate-jpa-2.1-api:jar:1.0.0.Final
com.fasterxml.jackson.datatype:jackson-datatype-hibernate4:jar:2.6.5
org.hibernate:hibernate-entitymanager:jar:5.1.0.Final
org.hibernate.common:hibernate-commons-annotations:jar:5.0.1.Final
org.hibernate:hibernate-java8:jar:5.1.0.Final
org.hibernate:hibernate-envers:jar:5.1.0.Final
Configuration Cache (on Spring boot application):
#Configuration
#EnableCaching
public class ApplicationCacheConfig extends CachingConfigurerSupport {
/**
* Configuration Table Cache
*/
public static final String CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME = "CONFIGURATION_TABLE_FIND_BY_ID_CACHE";
public static final String CONFIGURATION_TABLE_FIND_SERVICE_ID_CACHE_NAME = "CONFIGURATION_TABLE_FIND_SERVICE_ID_CACHE";
#Bean
#Override
public CacheManager cacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
Collection<Cache> caches = Lists.newArrayList();
caches.addAll(buildConfigurationCache());
simpleCacheManager.setCaches(caches);
return simpleCacheManager;
}
private Collection<Cache> buildConfigurationCache() {
List<Cache> caches = Lists.newArrayList();
// This cache never expires and don't have a maximum size because the table Configuration is not transactional
GuavaCache cacheFindById = new GuavaCache(CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME,
CacheBuilder.newBuilder().build());
caches.add(cacheFindById);
// This cache never expires and don't have a maximum size because the table Configuration is not transactional
GuavaCache cacheFindByService = new GuavaCache(CONFIGURATION_TABLE_FIND_SERVICE_ID_CACHE_NAME,
CacheBuilder.newBuilder().build());
caches.add(cacheFindByService);
return caches;
}
}
Hibernate entity:
#Entity
#Table(name = Configuration.TABLE_NAME)
#DynamicUpdate
public class Configuration implements Serializable {
public static final String TABLE_NAME = "configuration";
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id")
#Convert(converter = ConfigurationConverter.class)
private ConfigurationEnum id;
#Column(name = "service", nullable = false)
#NotNull
#Convert(converter = ServiceConverter.class)
private ServiceEnum service;
}
Repository (Spring-data):
public interface ConfigurationRepository extends PagingAndSortingRepository<Configuration, Integer>,
JpaSpecificationExecutor<Configuration> {
#Cacheable(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME)
Configuration findById(ConfigurationEnum configurationEnum);
#Cacheable(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_SERVICE_ID_CACHE_NAME)
List<Configuration> findByService(ServiceEnum service);
}
Configuration Enum:
#Getter
#AllArgsConstructor
public enum ConfigurationEnum {
CONFIG_1(1),
CONFIG_2(2);
private int id;
}
Configuration Converter:
#Converter
public class ConfigurationConverter implements AttributeConverter<ConfigurationEnum, Integer> {
#Override
public Integer convertToDatabaseColumn(ConfigurationEnum key) {
return key == null ? null : (int) key.getId();
}
#Override
public ConfigurationEnum convertToEntityAttribute(Integer key) {
return key == null ? null : Stream.of(ConfigurationEnum.values())
.filter(step -> key.equals(step.getId()))
.findFirst()
.orElse(null);
}
}
Test IT:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ApplicationIT.class)
#WebAppConfiguration
#Transactional
public class ConfigurationCacheIT {
#Autowired
ConfigurationRepository configurationRepository;
#Autowired
protected CacheManager cacheManager;
#Test
public void configuration_findById_cache_success() {
Configuration config = configurationRepository.findById(ConfigurationEnum.CONFIG_1);
// An ORM request is performed - CHECK
Assert.assertNotNull(step); // TEST OK
Cache.ValueWrapper entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1.getId());
Assert.assertNull(entry); OK
config = configurationRepository.findById(ConfigurationEnum.CONFIG_1);
// No ORM request is performed - CHECK
Assert.assertNotNull(step); // TEST OK
entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1.getId());
Assert.assertNotNull(entry); **// TEST FAIL !!!**
entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1.name());
Assert.assertNotNull(entry); **// TEST FAIL !!!**
entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1);
Assert.assertNotNull(entry); **// TEST FAIL !!!**
}
protected Cache.ValueWrapper getCacheEntry(String cacheName, Object key) {
return cacheManager.getCache(cacheName).get(key);
}
#Test
public void configuration_findByAll_without_cache_success() {
ArrayList<Configuration> list1 = Lists.newArrayList(configurationRepository.findAll());
// An ORM request is executed
Assert.assertNotNull(list1);
Assert.assertEquals(ConfigurationEnum.values().length, list1.size());
ArrayList<Configuration> list2 = Lists.newArrayList(configurationRepository.findAll());
// Another ORM request is executed
Assert.assertNotNull(list2);
Assert.assertEquals(ConfigurationEnum.values().length, list2.size());
}
}
My question is why my tests are failing?
Actually this was a non issue.
I'm using the fallowing architecture:
App-mdw (Middleware layer) (Spring boot App with #EnableCaching annotation)
App-ws (WebServices layer) (Spring boot App without #EnableCaching annotation)
The above tests were executed on the application App-ws and the annotation is not inherited that's why the caching was not working.
The right assert was:
entry = getCacheEntry(ApplicationCacheConfig.CONFIGURATION_TABLE_FIND_BY_ID_CACHE_NAME, ConfigurationEnum.CONFIG_1);
Assert.assertNotNull(entry)