Reputation: 39
Use redis as spring cache, exception when get object from redis. json data save to redis as below
{"@class":"cc.giveme5.auth.RoleEntity","id":10001,"name":"member","description":"注册用户","type":1,"permissions":["org.hibernate.collection.internal.PersistentSet",[{"@class":"cc.giveme5.auth.PermissionEntity","id":10001,"name":"member:read","description":"注册用户访问许可"}]]}
## env
spring-boot: 2.0.2.RELEASE
spring-data-redis: 2.0.7.RELEASE
jackson: 2.9.5
hibernate: 5.0.1
Spring Boot by default registers OpenEntityManagerInViewInterceptor to apply the “Open EntityManager in View” pattern in spring-boot(2.0.2.RELEAS) .
@Entity
@Table(name = "t_auth_role_m")
@JsonIgnoreProperties(ignoreUnknown = true)
public class RoleEntity implements Serializable
{
@Id
@TableGenerator(name = "UrRole_gen", table = "t_com_id_generator_r", pkColumnName = "seq_name", pkColumnValue = "UrRole_id", valueColumnName = "seq_value", allocationSize = Constants.SQE_ALLOCATION)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "UrRole_gen")
private Long id;
private String name;
private String description;
/**
* 角色对应许可
*/
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "t_auth_role_perm_r",
joinColumns = { @JoinColumn(name = "role_id", referencedColumnName = "id") },
inverseJoinColumns = { @JoinColumn(name = "perm_id", referencedColumnName = "id") })
private Set<PermissionEntity> permissions;
}
@Entity
@Table(name = "t_auth_perm_m")
@JsonIgnoreProperties(ignoreUnknown=true)
public class PermissionEntity implements Serializable {
@Id
private Long id;
@Column(length = 32, name = "perm_name", nullable = false)
@Size(max = 32)
private String name;
@Column(length = 128, name = "perm_description", nullable = false)
@Size(max = 128)
private String description;
}
@Configuration
@EnableCaching
public class CacheConfiguration {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
@ConfigurationProperties(prefix = "giveme5.cache")
public Properties4Cache cacheProperties(){
return new Properties4Cache();
}
@Bean
@Primary
public CompositeCacheManager cacheManager(){
CompositeCacheManager cacheManager = new CompositeCacheManager();
ArrayList<CacheManager> cacheManagers = new ArrayList<>();
//cacheManagers.add(simpleCacheManager());
cacheManagers.add(redisCacheManager(new redisObjectMapper(), redisConnectionFactory));
cacheManager.setCacheManagers(cacheManagers);
cacheManager.setFallbackToNoOpCache(true);
return cacheManager;
}
@Bean
public RedisCacheManager redisCacheManager(ObjectMapper objectMapper, RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(cacheProperties().getRedisCacheTtl()))
.computePrefixWith(cacheName -> cacheProperties().getName().concat(":").concat(cacheName).concat(":"))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)))
;
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration)
.build();
cacheManager.setTransactionAware(true);
return cacheManager;
}
public class redisObjectMapper extends ObjectMapper {
public redisObjectMapper() {
super();
// 将类型序列化到属性json字符串中
this.enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
this.registerModule(new Hibernate5Module().disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION));
// 对于找不到匹配属性的时候忽略报错
this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 不包含任何属性的bean也不报错
this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}
}
}
@Service
@Transactional
public class RoleService {
@Autowired
private IRoleRepository roleRepository;
@Cacheable(cacheNames = "default", key = "'role.' + #p0")
public RoleEntity role(Long roleId) {
return roleRepository.getOne(roleId);
}
}
public interface IRoleRepository extends BaseRepository<RoleEntity, Long, BaseVO> {
}
error has occurred in service :
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: cc.giveme5.auth.RoleEntity["permissions"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection, could not initialize proxy - no Session (through reference chain: cc.giveme5.auth.RoleEntity["permissions"])
at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:132) ~[spring-data-redis-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:110) ~[spring-data-redis-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:50) ~[spring-data-redis-2.0.7.RELEASE.jar:2.0.7.RELEASE]
at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:204)
...
Caused by: com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection, could not initialize proxy - no Session (through reference chain: cc.giveme5.auth.RoleEntity["permissions"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:391) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1704) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:290) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:189) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:130) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromAny(AsPropertyTypeDeserializer.java:193) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:712) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3079) ~[jackson-databind-2.9.5.jar:2.9.5]
at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:130) ~[spring-data-redis-2.0.7.RELEASE.jar:2.0.7.RELEASE]
... 96 common frames omitted
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:582) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:201) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:145) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:143) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:302) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:116) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromArray(AsArrayTypeDeserializer.java:53) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserializeWithType(CollectionDeserializer.java:314) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) ~[jackson-databind-2.9.5.jar:2.9.5]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288) ~[jackson-databind-2.9.5.jar:2.9.5]
... 106 common frames omitted
Upvotes: 3
Views: 3526
Reputation: 11
The problem is Redis sets the Collection type to "org.hibernate.collection.internal.PersistentSet". What you need is to change your Entity:
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "t_auth_role_perm_r",
joinColumns = { @JoinColumn(name = "role_id", referencedColumnName = "id") },
inverseJoinColumns = { @JoinColumn(name = "perm_id", referencedColumnName = "id") })
private Set<PermissionEntity> permissions = new HashSet<>();
@PostLoad
public void postLoad() {
this.permissions = new HashSet<>(this.permissions);
}
So Redis will set the field type to HashSet and the exception should gone.
Upvotes: 1
Reputation: 123
Hi . Hibernate init the lazy property when you call that, so permissions not init in load RoleEntity . After loading the root object by hibernate, in first time calling permissions , hibernate fetch data from database and load the permissions property. For initiation the lazy property, hibernate need a valid session. If hibernate session is closed , the object detached and could not init the lazy property and raise the LazyInitializationException For solving you can use: 1- set fetch type join. Load the property with root object in one select statement. 2- before closing session, call the permissions property for initiation. 3- implement a method for loading the permissions property from RoleEntity object.
Is this useful ?
A.Ayati
Upvotes: 1