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.
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.
Everyone I am new to neo4j and I am trying to enter Json into Neo4j but I am getting Match statement instead of create. Earlier I tried something myself and when When I inserted Json message only as
{"name":"john","dept":"Science"}
it went without a glitch but everytime I want try to add numeric data it gets error.
2020-03-10 13:21:59.793 INFO 94817 --- [ntainer#0-0-C-1] o.n.o.drivers.http.request.HttpRequest : Thread:
29, url: http://localhost:7474/db/data/transaction/92, request: {"statements":[{"statement":"UNWIND {rows}
as row **MATCH** (n) WHERE ID(n)=row.nodeId SET n:`UsersInfo` SET n += row.props RETURN row.nodeId as ref,
ID(n) as id, {type} as type","parameters":{"type":"node","rows":[{"nodeId":23,"props":{"name":"raj",
"dept":"science","age":11}}]},"resultDataContents":["row"],"includeStats":false}]}
These are my classes
KafkaConfiguration
#EnableKafka
#Configuration
public class KafkaConfiguration {
#Bean
public ConsumerFactory<String, Users> userConsumerFactory(){
Map<String, Object> config = new HashMap<>();
config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
config.put(ConsumerConfig.GROUP_ID_CONFIG, "group_json");
config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
return new DefaultKafkaConsumerFactory<>(config, new StringDeserializer(),
new JsonDeserializer<>(Users.class));
}
#Bean
public ConcurrentKafkaListenerContainerFactory<String, Users> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, Users> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(userConsumerFactory());
return factory;
}
}
KafkaConsumer class
Service
public class KafkaConsumer {
#Autowired
public Neo4jservice neo4jService;
#KafkaListener(topics = "UsersJson", groupId = "group_id", containerFactory = "kafkaListenerContainerFactory")
public void consume(Users users) {
System.out.println("Consumed message: " + users);
UsersInfo usern = new UsersInfo();
usern.setAge(users.getAge());
usern.setDept(users.getDept());
usern.setId(users.getId());
usern.setName(users.getName());
neo4jService.saveIntoStudentsTable(usern);
}
}
Neo4jService
#Service
public class Neo4jservice {
#Autowired
private UsersRepo userRepo;
public UsersInfo saveIntoStudentsTable(UsersInfo users) {
UsersInfo usern = userRepo.save(users);
return (usern);
}
}
UsersRepo
#Repository
public interface UsersRepo extends Neo4jRepository<UsersInfo, Long>{
}
Users class
public class Users {
private Long id;
private String name;
private String dept;
private Integer age;
**getters,setters and toString method here**
}
Likewise UsersInfo class
#NodeEntity
public class Users {
#Id
private Long id;
private String name;
private String dept;
private Integer age;
**getters,setters and toString method here**
}
Any help will be greatly appreciated. Thanks
You are setting also the id value of the User class.
This will make Spring Data Neo4j and the Neo4j Object Graph Mapper that is used for the persistence think that the entity already exists.
In this case it will MATCH on an existing id(n) and update the properties as you can see in the logs instead of CREATE a new node.
I use spring boot 2.1.2 and redis as cache provider.
But now, I have a question.
In sysUser entity
#Data
#Entity
#Table(name = "sys_user")
#ToString(exclude = "roles")
#EqualsAndHashCode(callSuper = true)
#Proxy(lazy = false)
public class SysUser extends BaseEntity implements UserDetails {
// ...
/**
* 当前用户的权限
*/
#ManyToMany(fetch = FetchType.EAGER)
#JsonIgnoreProperties(value = "users")
#JoinTable(name = "sys_user_role",
joinColumns = {#JoinColumn(name = "user_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "role_id", nullable = false)})
private List<SysRole> roles;
// ...
}
In sysRole entity
#Data
#Entity
#Table(name = "sys_role")
#EqualsAndHashCode(callSuper = true)
#ToString(exclude = {"users", "permissions"})
#Proxy(lazy = false)
public class SysRole extends BaseEntity {
// ...
/**
* 当前角色的菜单
*/
#JsonIgnoreProperties(value = "roles")
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinTable(name = "sys_permission_role", joinColumns = #JoinColumn(name = "role_id"),
inverseJoinColumns = #JoinColumn(name = "permission_id"))
private List<SysPermission> permissions = new ArrayList<>();
/**
* 当前角色对应的用户
* 双向映射造成数据重复查询死循环问题
*/
#ManyToMany(mappedBy = "roles")
private List<SysUser> users = new ArrayList<>();
}
In SysPermission entitty
#Data
#Entity
#Table(name = "sys_permission")
#EqualsAndHashCode(callSuper = true)
#Proxy(lazy = false)
public class SysPermission extends BaseEntity {
// ...
/**
* 菜单角色
* 双向映射造成数据重复查询死循环问题
*/
#ManyToMany(mappedBy = "permissions")
private List<SysRole> roles = new ArrayList<>();
}
In sysUser service impl
#Override
#Cacheable
public SysUser loadUserByUsername(String username) {
return sysUserRepository.findFirstByUsernameAndEnabledTrue(username).orElseThrow(() ->
new UsernameNotFoundException("用户不存在")
);
}
redis config
#Bean
#Override
public CacheManager cacheManager() {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(12))
.prefixKeysWith(applicationProperties.getName())
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
.disableCachingNullValues();
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory))
.cacheDefaults(redisCacheConfiguration)
.transactionAware()
.build();
}
#Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
return redisTemplate;
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
Question
When I first called loadUserByUsername,it is ok.And in redis
in json.cn
But when I secound called loadUserByUsername,it is wrong,And get exception
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: failed to lazily initialize a collection, could not initialize proxy - no Session (through reference chain: cn.echocow.xiaoming.model.entity.SysUser["roles"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection, could not initialize proxy - no Session (through reference chain: cn.echocow.xiaoming.model.entity.SysUser["roles"])
at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:132)
at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:110)
at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:48)
......
Caused by: com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection, could not initialize proxy - no Session (through reference chain: cn.echocow.xiaoming.model.entity.SysUser["roles"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:353)
......
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:597)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:216)
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:160)
at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:287)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:302)
......
Other
I try these methods
#JsonIgnore , but it will set roles is null, I want to use this field.
Config jackson registerModule Hibernate5Module, it will set roles is null.
Use #Proxy(lazy = false), no changes.
Use #ManyToMany(fetch = FetchType.EAGER), no changes
config
spring:
jpa:
open-in-view: true
properties
hibernate:
enable_lazy_load_no_trans: true
no changes...
Use another json tools, such as gson and FastJson, but infinite loop for jpa when save cache.
Please help me, I had spent three days...But I do not resolve this question...
Thanks!
github address: XIAOMING
If do not have resolve method, maybe I must use Mybatis. But there is a lot of work.Please help me resolve this question...
1st. create 2 classes below
The HibernateCollectionIdResolver.class will translate HibernateCollection class into JDK collection class, so the Jackson will write json from
{
"paramA": [
"org.hibernate.collection.internal.PersistentSet",
[]
]
}
to
{
"paramA": [
"java.util.HashSet",
[]
]
}
then the method typeFromId will get JDK JavaType from the class full name above, to deserialize your json to POJO.
class HibernateCollectionIdResolver extends TypeIdResolverBase {
public HibernateCollectionIdResolver() {
}
#Override
public String idFromValue(Object value) {
//translate from HibernanteCollection class to JDK collection class
if (value instanceof PersistentArrayHolder) {
return Array.class.getName();
} else if (value instanceof PersistentBag || value instanceof PersistentIdentifierBag || value instanceof PersistentList) {
return List.class.getName();
} else if (value instanceof PersistentSortedMap) {
return TreeMap.class.getName();
} else if (value instanceof PersistentSortedSet) {
return TreeSet.class.getName();
} else if (value instanceof PersistentMap) {
return HashMap.class.getName();
} else if (value instanceof PersistentSet) {
return HashSet.class.getName();
} else {
//default is JDK collection
return value.getClass().getName();
}
}
#Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
return idFromValue(value);
}
//deserialize the json annotated JDK collection class name to JavaType
#Override
public JavaType typeFromId(DatabindContext ctx, String id) throws IOException {
try {
return ctx.getConfig().constructType(Class.forName(id));
} catch (ClassNotFoundException e) {
throw new UnsupportedOperationException(e);
}
}
#Override
public JsonTypeInfo.Id getMechanism() {
return JsonTypeInfo.Id.CLASS;
}
}
#JsonTypeInfo(
use = JsonTypeInfo.Id.CLASS
)
#JsonTypeIdResolver(value = HibernateCollectionIdResolver.class)
public class HibernateCollectionMixIn {
}
2nd. register this MixIn class to you ObjectMapper
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
mapper.registerModule(new Jdk8Module());
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new JodaModule());
mapper.addMixIn(Collection.class, HibernateCollectionMixIn.class);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
last, register your jackson2JsonRedisSerializer to your RedisCacheConfiguration.
This would be helpful, I spent 2 days researching how to solve this problem.
And I found the json type id could be rewrite...
So just override jackson typeIdResolver~
EDIT: solve deserialization issue and add some comments
In your code you return valueSerializer like this
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
But you will have to return the GenericJackson2JsonRedisSerializer with Jackson Object mapper that has Hibernate5Module or Hibernate4Module registered as a module
public ObjectMapper getMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
mapper.enable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// Registering Hibernate5Module to support lazy objects for hibernate 5
// Use Hibernate4Module if using hibernate 4
mapper.registerModule(new Hibernate5Module());
return mapper;
}
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer(getMapper());
}
this is class A
#Document
class User{
private String id ;
private String name;
#Dbref
private List<Socity> Socitys;
}
and this is class Socity
#Document
class Socity{
private String id ;
private String name;
}
and this is the add user function
public User addUser(User user) {
List<Socity> socity = new ArrayList<>();
user.setsocitys (socity );
return userRepository.save(user);
}
I want to add a socity to an existing user
i try this but it doesn't work
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run (App.class, args);
SocityDao SDao = ctx.getBean(SocityDao .class);
UserRepository userRepository = ctx.getBean(UserRepository.class);
User u = userRepository.findOne("");
Socity s = new Socity("soc1");
SDao .addSocity(e);
u.getSocitys().add(e);
}
this is the rest service
#RequestMapping(value = "up/{id}", method = RequestMethod.POST ,produces =
"application/json")
public User addSocityToUser(#RequestBody Socity, #PathVariable String id)
{
return SocityDAO.addSocityToUser(e, id);
}
In the end of your code add userRepository.save(u) to persist your changes to the DB.
As long as it as an ID (because it is a persisted object) it will be updated. if it has no ID it will be saved as a new object in the DB.
Looks like you forget to save user, after you add new socity. Please check my updates
#Document
public class Socity {
private String id ;
private String name;
}
#Document
public class User {
private String id;
private String name;
#DBRef
private List<Socity> socitys = new ArrayList<>();
}
Then you don't need to use your addUser() method. When you want to add new user just use
userRepository.save(user);
You also need two repositories
public interface SocityRepository extends MongoRepository<Socity, String> {
}
and
public interface UserRepository extends MongoRepository<User, String> {
}
And what you need in the main method
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run (App.class, args);
UserRepository userRepository = ctx.getBean(UserRepository.class);
SocityRepository socityRepository = ctx.getBean(SocityRepository.class);
User u = userRepository.findOne("");
Socity s = socityRepository.save(new Socity("soc1"));
u.getSocitys().add(s);
userRepository.save(u);
}
It is always better to Use the MongoTemplate to write an update Query and use Push function to add to a list.
#Autowired
private MongoTemplate mongoTemplate;
function(String id, Socity socity){
Query query = new Query(Criteria.where("id").is(id));
Update update = new Update();
update.push("Socitys", socity);
mongoTemplate.updateFirst(query, update, User.class);
}
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)