9

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

当我 EAGERLY 加载 @OneToMany 集合字段时一切正常,但是当我尝试访问 LAZY 加载的元素时,我发现 HibernateSessionImplementor对象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>();

如何绑定休眠会话以便在@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

4 回答 4

11

我遇到了同样的问题,花了几天的时间试图找到解决方案,终于找到了解决方案。我想分享我为那些可能有同样问题的人找到的详细信息。

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

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

所以最好的办法是

@Controller
ControllerBean {

    @Autowired
    AsyncService asyncService;

    public controllerMethod() {
        asyncService.asyncMethod();
    }
}

@Service
AsyncService {
    @Autowired
    TransactionalService transactionalService;

    @Async
    public asyncMethod() {
        transactionalService.transactionalMethod();
    }
}

@Service
TransactionalService {
    @Autowired
    SomeOtherService someOtherService;

    @Autowired
    EntityRepository entityRepository;

    @Transactional
    public transactionalMethod() {
        Entity entity = entityRepository.findOne(12345);

        someOtherService.doWork(entity);
    }
}

@Service
SomeOtherService {

    @Autowired
    EntityRepository entityRepository;

    @Transactional
    public doWork(Entity entity) {
        // fetch lazy properties, save, etc. without any session problems...
        entity.getLazyProperties(); 

        entityRepository.save(entity);
    }
}
于 2014-11-27T12:43:25.190 回答
9

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

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

我引用 Spring 的JpaTransactionManager文档:

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

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

有关更多信息,请参阅此问题

于 2014-08-01T15:17:19.503 回答
1

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

@TransactionalSpring@Component调用使用@Asyncthis 注释的方法时不会发生。对异步方法的调用由任务执行器在稍后时间安排和执行,因此作为“新”调用处理,即没有事务上下文。如果@Async方法(或声明它的组件)@Transactional本身不是 Spring 将不会管理任何需要的事务。

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

于 2014-08-01T15:20:33.483 回答
0

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

如果要同时使用@AsyncLAZY加载,则使用@Transactional声明的方法必须实现LAZY加载的逻辑。

如果LAZY加载是在@Transactional之外启动的,它将不起作用。

于 2019-12-02T14:40:30.903 回答