spring 缓存 redis:LazyInitializationException 无法延迟初始化集合,无法初始化代理 - 无会话

问题描述 投票:0回答:2

使用redis作为spring缓存,从redis获取对象时出现异常。 json数据保存到redis如下

{"@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,“名称”:“成员:读取”,“描述”: "注册用户访问权限"}]]}

## env
spring-boot: 2.0.2.RELEASE
spring-data-redis: 2.0.7.RELEASE
jackson: 2.9.5
hibernate: 5.0.1

Spring Boot 默认注册 OpenEntityManagerInViewInterceptor 以在 spring-boot(2.0.2.RELEAS) 中应用“在视图中打开 EntityManager”模式。

实体

@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> {
}

例外

服务中发生错误:

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
java hibernate spring-boot redis jackson
2个回答
1
投票

嗨. 当您调用该属性时,Hibernate 会初始化惰性属性,因此 permissions 不会在 load RoleEntity 中初始化。 通过 hibernate 加载根对象后,在第一次调用 permissions 时,hibernate 从数据库获取数据并加载 permissions 属性。 为了启动惰性属性,hibernate 需要一个有效的会话。如果休眠会话关闭,则对象分离并且无法初始化惰性属性并引发LazyInitializationException 为了解决你可以使用: 1-设置获取类型join。在一个 select 语句中加载带有根对象的属性。 2- 在关闭会话之前,调用 permissions 属性进行启动。 3- 实现从 RoleEntity 对象加载 permissions 属性的方法。

这个有用吗?

A.阿亚蒂


0
投票

问题是 Redis 将 Collection 类型设置为“org.hibernate.collection.internal.PersistentSet”。您需要的是更改您的实体:

@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);
}

因此 Redis 会将字段类型设置为 HashSet,并且异常应该消失。

© www.soinside.com 2019 - 2024. All rights reserved.