@Transactional 上的 Spring Hibernate LazyInitializationException

Posted

技术标签:

【中文标题】@Transactional 上的 Spring Hibernate LazyInitializationException【英文标题】:Spring Hibernate LazyInitializationException on @Transactional 【发布时间】:2017-02-23 04:10:48 【问题描述】:

我有一个在启动时运行异步任务的服务,它以递归方式运行:

@Service
public class TaskService 

    private final TaskRepository taskRepository;

    @Inject
    public TaskService(TaskRepository taskRepository) 
        this.taskRepository= taskRepository;
    

    private final int currentTaskId = -1;

    @Transactional
    @PostConstruct
    private void init() 
        taskRepository.findByClosedDateIsNull().forEach(taskRepository::delete);
        runTask();
    

    @Async
    @Transactional
    private void runTask() 
        if (!getCurrent().isPresent()) 
            Task task = new Task();
            //set props
            currentTaskId = taskRepository.save(task).getId(); 
        
        Util.sleep(5000); //wrapper for simple Thread.sleep(long l).
        Task task = getCurrent().get();
        if (task.getEvents().size > 0) 
            //bussiness logic
            Util.sleep(1000);
        
        runTask();
    

    @Transactional(readOnly = true)
    private Optional<Task> getCurrent() 
        return taskRepository.findOneById(currentTaskId).map(task -> 
            task.getEvents().size(); //throws the error here
            return task;
        );
    


堆栈跟踪:

原因:org.hibernate.LazyInitializationException:未能延迟初始化角色集合:com.test.domain.Task.events,无法初始化代理 - 没有会话 在 org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:576) 在 org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:215) 在 org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:156) 在 org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:160) 在 com.test.service.TaskService.lambda$getCurrent$5(TaskService.java:135) 在 java.util.Optional.map(Optional.java:215) 在 com.test.service.TaskService.getCurrent(TaskService.java:134) 在 com.test.service.TaskService.runTask(TaskService.java:163) 在 com.test.service.TaskService.init(TaskService.java:66) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:498) 在 org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:365) 在 org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:310) 在 org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133) ...省略了21个常用框架

我也尝试过 Hibernate.initialize(Object proxy);。 OpenViewSessionFilter 不是我的解决方案。我不想设置这个集合 EAGER。

【问题讨论】:

为什么OpenViewSessionFilter 不是解决方案?您可以使用获取计划并使其急于执行此查询吗? OpenViewSessionFilter 会降低性能。 + 其他服务中的大多数方法(如getCurrent())都有效。我认为问题在@Async,很多@Transactional 还是递归的。因为我使用 JpaRepository,所以我不能急于求成。如果它是惰性的,它就不能返回急切的集合。我正在尝试在 getCurrent() 方法中获取集合。 您正在处理程序中执行显式sleep。过滤器的开销相关。 这是sleep 方法(也许是Spring-way)使其工作的一种方式吗?不睡觉怎么办? First Spring 使用代理来处理事务和异步行为。所以基本上你在私有方法上的@ASync@Transactional 是没用的(方法调用不通过代理)。其次,没有保证 init 方法上的 @Transactional 已经应用(它可能会或可能不会)。基本上在@PostConstruct 方法中做这样的事情并不是正确的做法。除此之外,您最终还将遇到与您现在设置代码的方式有关的堆栈溢出。从runTask 调用runTask 最终会中断。 【参考方案1】:

我找到了一个解决方案(不知道这是否是一种不好的做法,但它对我有用)。只需在您的 JpaRepository 中创建一个默认方法并使用 @Transactional 对其进行注释。在服务中调用这个方法。

【讨论】:

不错的答案!逻辑保留在存储库中【参考方案2】:

对我来说,当我在第一个函数调用中删除 @Transactional(在您的情况下为 init())并将 @Async 功能移动到单独的 bean 时,它解决了问题。

【讨论】:

以上是关于@Transactional 上的 Spring Hibernate LazyInitializationException的主要内容,如果未能解决你的问题,请参考以下文章

Spring @Transactional 使用

@Transactional 上的 MySQL 套接字超时行为

Spring @Transactional 和 Spring @Lock 注解有啥关系?

Spring 忽略 @Transactional 注释

实体退出@Transactional上下文时的Spring回调?

Spring 事务 -- @Transactional的使用