Spring @Async:LAZY集合上的null hibernate会话

时间:2014-08-01 15:11:16

标签: java spring hibernate hibernate-session

我在服务图层方法上使用@Async注释。

当我EAGERLY加载@OneToMany集合字段时,一切正常,但是当我尝试访问加载了LAZY的元素时,我发现Hibernate SessionImplementor对象session为空。这显然给了我一个例外:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:
....    

这是我的收集字段:

@OneToMany(mappedBy="abc", fetch=FetchType.LAZY, cascade=CascadeType.REMOVE)
@OrderBy(value="xsd asc")
@JsonIgnore
private Set<Item> items = new HashSet<Item>();

如何绑定hibernate会话以便在@Async上下文中加载我的对象?

修改

这是我的trancactionManager / entityManager配置

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emf"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter">

    </property>
    <property name="packagesToScan" value="it.domain"/>

    <property name="persistenceUnitName" value="persistenceUnit"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
            <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
            <!--${hibernate.format_sql} -->
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            <!-- ${hibernate.show_sql} -->
            <prop key="hibernate.show_sql">false</prop> 

            <prop key="hibernate.connection.charSet">UTF-8</prop>

            <prop key="hibernate.max_fetch_depth">3</prop>
            <prop key="hibernate.jdbc.fetch_size">50</prop>
            <prop key="hibernate.jdbc.batch_size">20</prop>

            <prop key="org.hibernate.envers.audit_table_suffix">_H</prop>
            <prop key="org.hibernate.envers.revision_field_name">AUDIT_REVISION</prop>
            <prop key="org.hibernate.envers.revision_type_field_name">ACTION_TYPE</prop>
            <prop key="org.hibernate.envers.audit_strategy">org.hibernate.envers.strategy.ValidityAuditStrategy</prop>
            <prop key="org.hibernate.envers.audit_strategy_validity_end_rev_field_name">AUDIT_REVISION_END</prop>
            <prop key="org.hibernate.envers.audit_strategy_validity_store_revend_timestamp">True</prop>
            <prop key="org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name">AUDIT_REVISION_END_TS</prop>               
        </props>
    </property>
</bean>

<jpa:repositories base-package="it.repository"
                  entity-manager-factory-ref="emf"
                  transaction-manager-ref="transactionManager"/>

<jpa:auditing auditor-aware-ref="auditorAwareBean" />
<bean id="auditorAwareBean" class="it.auditor.AuditorAwareBean"/>

4 个答案:

答案 0 :(得分:10)

我遇到了同样的问题,花了几天时间试图找到解决方案,最后得到了解决方案。我想与那些可能遇到同样问题的人分享我找到的细节。

1st - 您的@Async - 带注释的方法应该在单独的bean中声明,而不是@Controller - 或@RestController - 带注释的bean。

第二 - 你当然需要声明从@Transactional声明的方法中调用的方法@Async。但是,必须定义从@Async方法调用的第一个方法@Transactional。我在方法执行堆栈中有二级或三级{{​​1}}方法,因此问题没有解决,我花了两天时间试图弄清楚发生了什么。

所以最好的办法是

@Transactional

答案 1 :(得分:5)

使用ThreadLocals保留Spring的事务上下文。这意味着您的SessionFactory仅可用于调度您的请求的线程,因此,如果您创建新线程,您将获得null和相应的异常。

您的@Async方法所做的是使用TaskExecutor在另一个线程中运行您的方法。因此,您的服务就会出现上述问题。

我引用Spring的JpaTransactionManager文档:

  

单个JPA的PlatformTransactionManager实现   EntityManagerFactory的。 绑定指定的JPA EntityManager   工厂到线程,可能允许一个线程绑定   每个工厂的EntityManager。 SharedEntityManagerCreator和   @PersistenceContext知道线程绑定的实体管理器和   自动参与此类交易。使用其中之一   支持此事务管理的JPA访问代码所必需的   机构。

如果你想保留你的注释,你应该看看Hibernate CurrentSessionContext并以某种方式自己管理会话。

有关详细信息,请参阅this question

答案 2 :(得分:1)

在正常情况下(没有@Async),事务会通过调用层次结构从一个Spring组件传播到另一个组件。

@Transactional Spring @Component调用使用@Async注释的方法时,这不会发生。异步方法的调用正在由任务执行程序稍后安排和执行,因此作为一个新的&#39;处理。呼叫,即没有事务上下文。如果@Async方法(或声明它的组件)本身不是@Transactional,Spring将无法管理任何所需的事务。

尝试注释调用@Async方法的方法,并告诉我们是否有效。

答案 3 :(得分:0)

这取决于映射的方式和位置。

如果要与 LAZY 加载一起使用 @Async ,则用 @Transactional 声明的方法必须实现 LAZY 加载。

如果 LAZY 加载是在 @Transactional 外部启动的,则它将无法正常工作。