JPA LazyInitialisationException - 无法初始化代理

Posted

技术标签:

【中文标题】JPA LazyInitialisationException - 无法初始化代理【英文标题】:JPA LazyInitialisationException - Could Not Initialize Proxy 【发布时间】:2019-07-04 12:25:15 【问题描述】:

是的,我见过类似的问题,但没有一个答案真正让我找到了解决方案。我没有在任何地方使用该线程,并且相同的代码在另一个 jhipster 应用程序中工作,所以我很难过为什么这个提升+移位会导致休眠问题。

public UserDetailsContextMapper userDetailsContextMapper() 
    return new UserDetailsContextMapper() 
        @Override
        public UserDetails mapUserFromContext(
            DirContextOperations ctx, String username,
            Collection<? extends GrantedAuthority> authorities) 
            log.error("2 " + username + " -> " + ctx.toString());
            String lowercaseLogonName = username.toLowerCase();
            Optional<User> userFromDatabase = userRepository.findOneByLogin(lowercaseLogonName);
            if (!userFromDatabase.isPresent()) 
                User ldapUser = new User();
                ldapUser.setLogin(lowercaseLogonName);
                ldapUser.setPassword(RandomStringUtils.random(60)); // We use LDAP password, but the password need to be set
                ldapUser.setActivated(true);
                try 
                    Attribute attribute = ctx.getAttributes().get("mail");
                    ldapUser.setEmail( attribute != null && attribute.size() > 0 ? (String) attribute.get(0) : "");
                    attribute = ctx.getAttributes().get("givenname");
                    ldapUser.setFirstName(attribute != null && attribute.size() > 0 ? (String) attribute.get(0) : "");
                    attribute = ctx.getAttributes().get("sn");
                    ldapUser.setLastName(attribute != null && attribute.size() > 0 ? (String) attribute.get(0) : "");
                 catch (NamingException e) 
                    log.warn("Couldn't get LDAP details for user: " + lowercaseLogonName);
                

                Set<Authority> newUserAuthorities = new HashSet<>();
                Authority authority = authorityRepository.getOne(AuthoritiesConstants.USER);

                newUserAuthorities.add(authority); <-- This line in the Exception

                ldapUser.setAuthorities(newUserAuthorities);
                ldapUser.setLangKey("en");
                userRepository.saveAndFlush(ldapUser);

            

例外:

org.hibernate.LazyInitializationException: could not initialize proxy [com.app.my.let.domain.Authority#ROLE_USER] - no Session
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:155)
        at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:268)
        at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73)
        at com.app.my.let.domain.Authority_$$_jvst758_1e.hashCode(Authority_$$_jvst758_1e.java)
        at java.util.HashMap.hash(HashMap.java:339)
        at java.util.HashMap.put(HashMap.java:612)
        at java.util.HashSet.add(HashSet.java:220)
        at com.app.my.let.config.SecurityConfiguration$1.mapUserFromContext(SecurityConfiguration.java:249) <-- code line above

JPA 属性:

jpa:
        database-platform: org.hibernate.dialect.SQLServer2012Dialect
        database: SQL_SERVER
        show_sql: true
        format_sql: true
        properties:
            hibernate.id.new_generator_mappings: true
            hibernate.cache.use_second_level_cache: false
            hibernate.cache.use_query_cache: false
            hibernate.generate_statistics: true

编辑 - 这解决了我的问题:

Optional<Authority> optAuthority = authorityRepository.findById(AuthoritiesConstants.USER);

Authority authority = optAuthority.get();

【问题讨论】:

您何时打开 JPA 会话? AuthorityRepository 是打开还是关闭会话? 很确定会话只是在应用程序启动时打开。在旧应用程序(有效)或新应用程序(无效)中,从来没有任何代码可用于 openSession 或 getCurrentSession。从上面的 yml 文件中添加了 jpa 属性。 Hibernate 使用多个会话。进行一次长时间运行的会话是不好的做法。我对 JHipster 并不特别熟悉,但通常每个请求至少有一个会话,并且每个复杂请求通常有多个会话。可以发authorityRepository.getOne的代码吗?您正在将对象添加到哈希集中。该错误很可能意味着您在哈希码函数中使用的某些内容在会话关闭之前没有被 Hibernate 初始化。 Authority 有关联吗? 这是一个通用的空类。包 com.app.my.let.repository;导入 com.app.my.let.domain.Authority;导入 org.springframework.data.jpa.repository.JpaRepository; /** * 授权实体的 Spring Data JPA 存储库。 */ 公共接口 AuthorityRepository 扩展 JpaRepository 那么是的,即关闭会话***.com/a/25710391/1245752。 Authority.hashCode是如何实现的? 【参考方案1】:

这一行:

Authority authority = authorityRepository.getOne(AuthoritiesConstants.USER);

只为您提供对实体的引用。在引擎盖下它调用EntityManager#getReference。如前所述,这不是实体,只是用于对象关系映射目的的参考。抛出异常,因为您试图将其转换到事务环境之外。

有几种解决方案。要获取实体,您必须使用EntityManager#find 或在事务中执行(在方法上使用@Transactional)。在这里你也可以调用Hibernate.initialize(authority)从数据库中获取实体。

【讨论】:

啊,埋在规范文档而不是 API 文档中……我实际上只是使用 findById().get() 解决了它,它确实获取了实际数据。

以上是关于JPA LazyInitialisationException - 无法初始化代理的主要内容,如果未能解决你的问题,请参考以下文章

JPA 版本混淆

认识JPA以及如何使用JPA

Jpa使用详解

Jpa使用详解

JPA

JPA   初始JPA