【问题标题】:LazyInitializationException trying to get lazy initialized instanceLazyInitializationException 试图获取惰性初始化实例
【发布时间】:2017-07-26 17:41:53
【问题描述】:

当我尝试获取惰性初始化实体时,我在 IDE 中看到以下异常消息(我找不到它在代理实体中的存储位置,因此我无法提供此异常的整个堆栈跟踪):

Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate com.epam.spring.core.domain.UserAccount_$$_jvste6b_4.toString()

这是我在尝试访问要使用的惰性初始化实体的字段后立即得到的堆栈跟踪:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165)

    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)

    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)

    at com.epam.spring.core.domain.UserAccount_$$_jvstfc9_4.getMoney(UserAccount_$$_jvstfc9_4.java)

    at com.epam.spring.core.web.rest.controller.BookingController.refill(BookingController.java:128) 

我正在使用 Spring Data,配置了 JpaTransactionManager,数据库是 MySql,ORM 提供者是 Hibernate 4。注释 @EnableTransactionManagement 已打开,@Transactional 被放置在我能想象到的任何地方,但没有任何效果。

这是一个关系:

@Entity
public class User extends DomainObject implements Serializable {

    ..

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "user_fk")
    private UserAccount userAccount;

    ..

@Entity
public class UserAccount extends DomainObject {

    ..

    @OneToOne(mappedBy = "userAccount")
    private User user;

    ..

..一个配置:

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getRequiredProperty(PROP_NAME_DATABASE_DRIVER));
        dataSource.setUrl(env.getRequiredProperty(PROP_NAME_DATABASE_URL));
        dataSource.setUsername(env.getRequiredProperty(PROP_NAME_DATABASE_USERNAME));
        dataSource.setPassword(env.getRequiredProperty(PROP_NAME_DATABASE_PASSWORD));
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
        entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN));
        entityManagerFactoryBean.setJpaProperties(getHibernateProperties());            
        return entityManagerFactoryBean;
     }

    @Bean
    public JpaTransactionManager transactionManager(@Autowired DataSource dataSource,
                                                    @Autowired EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
        jpaTransactionManager.setDataSource(dataSource);

        return jpaTransactionManager;
    }

.. 这就是我想要检索 UserAccount 的方式:

    @RequestMapping(...)
    @Transactional()
    public void refill(@RequestParam Long userId, @RequestParam Long amount) {
        User user = userService.getById(userId);
        UserAccount userAccount = user.getUserAccount();
        userAccount.setMoney(userAccount.getMoney() + amount);
    }

Hibernate 版本是 4.3.8.Final,Spring Data 1.3.4.RELEASE 和 MySql 连接器 5.1.29。

请问我是否需要其他东西。提前谢谢!

【问题讨论】:

  • 这部分堆栈跟踪实际上什么也没显示。
  • @v.ladynev 我应该提供整个堆栈吗?
  • 这个Method threw org.hibernate.LazyInitializationException的一部分
  • @v.ladynev 请查看更新的问题

标签: mysql hibernate spring-data lazy-loading spring-transactions


【解决方案1】:

我收到了这个错误:

方法抛出 'org.hibernate.LazyInitializationException' 异常。

这是因为当前没有会话存在。 Hibernate 打开一个会话并关闭它,但是对于“lazy = true”或“fetch = FetchType.LAZY”,这些字段由代理填充。当您尝试查找此类字段的值时,它将尝试使用活动会话访问数据库以检索数据。如果找不到此类会话,则会出现此异常。

您可以使用“lazy=false”修复它或检查您是否正确使用了@Transcational(尝试在您的服务层而不是数据访问层中使用它),您也可以使用

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

@Transactional

【讨论】:

    【解决方案2】:

    我在运行 springBoot 应用程序时也遇到了同样的错误。

    这里真正的问题是什么?

    • 请检查您是否在控制器级别自动装配了存储库
    • 如果第一步正确,请检查您在哪里自动装配了 JPA 存储库,它应该是 @Transactional 代码的一部分。
    • 如果没有,请添加@Transactional 注释。它将解决您的问题。

    【讨论】:

      【解决方案3】:

      正如 @v.ladynev 简要解释的那样,您的问题是您想在持久性上下文之外初始化一个惰性关系。

      我写了一篇关于这个的文章,你可能会觉得有帮助:https://arnoldgalovics.com/lazyinitializationexception-demystified/

      【讨论】:

      • 谢谢,我会看的!但对我来说最有趣的部分是 Spring 没有任何代理控制器添加事务行为。
      • 有很多方法可以做到这一点,例如,您可以使用过滤器自动为每个请求添加事务上下文。这不是一个好主意的原因是事务应该代表业务逻辑方面的工作单元,而不是处理请求。
      【解决方案4】:

      我用下面来修复 sessionFactory.getObject().getCurrentSession() 创建查询并获取所需对象

      【讨论】:

        【解决方案5】:

        首先,您应该了解问题的根源不是交易。我们有一个事务和一个持久的上下文(会话)。使用@Transactional 注解,Spring 创建一个事务并打开持久化上下文。在方法被调用后,一个持久化的上下文就关闭了。

        当您调用user.getUserAccount() 时,您有一个包装UserAccount 的代理类(如果您不使用User 加载UserAccount)。因此,当一个持久上下文关闭时,在调用UserAccount 的任何方法期间,您都会有一个LazyInitializationException,例如user.getUserAccount().toString()

        @Transactional 在您的情况下仅在 userService 级别上工作。要使@Transactional 工作,将@Transactional 注释放在方法上是不够的。您需要使用来自Spring Context 的方法获取类的对象。因此,要更新资金,您可以使用其他服务方法,例如updateMoney(userId, amount)

        如果你想在控制器方法上使用@Transactional,你需要从Spring Context 获取一个控制器。 Spring 应该明白,它应该用一个特殊的方法来包装每个@Transactional 方法来打开和关闭一个持久化上下文。另一种方法是使用 Session Per Request Anti 模式。您需要添加一个特殊的 HTTP 过滤器。

        https://vladmihalcea.com/the-open-session-in-view-anti-pattern/

        【讨论】:

        • 谢谢,现在我在初始化实体对象时看到了延迟加载的问题。但是如何修复初始化?
        • 但是为什么我不能在控制器方法中使用事务呢?有什么区别?但让我试试你的建议。
        • @DmitrySenkovich 欢迎您。我添加了一些额外的解释。
        • @SalmanS 这个异常仅仅意味着当你调用一个持久对象的方法时你没有一个打开的Session或者Persistent Context
        • @SalmanS Session 不是线程安全的。所以你不能在并发环境中使用它。 Hibernate 如何获取当前会话由属性控制。其中一种方法是使用ThreadLocal 将会话绑定到线程。可能这就是问题的原因。 stackoverflow.com/questions/8046662/…
        【解决方案6】:

        尽管存在性能问题,但要快速解决问题,请在您的服务中使用 @transactional 示例:

        @Transactional
        public TPage<ProjectDto> getAllPageable(Pageable pageable) {
            Page<Project> data = projectRepository.findAll(pageable);
            TPage<ProjectDto> response = new TPage<>();
            response.setStat(data, Arrays.asList(modelMapper.map(data.getContent(), ProjectDto[].class)));
            return response;
        }
        

        它将在第二个查询中获取项目经理的用户详细信息。 如需更高级的解决方案,您应该阅读@galovics 答案中的博文。

        【讨论】:

          猜你喜欢
          • 2021-11-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多