I'm using an @Async
annotation on a service layer method.
Everything works fine when I EAGERLY load @OneToMany collection fields, but when I try to access LAZY loaded element I found that Hibernate SessionImplementor
object session
is null. That obviously give me an exception:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:
....
Here is my collection field:
@OneToMany(mappedBy="abc", fetch=FetchType.LAZY, cascade=CascadeType.REMOVE)
@OrderBy(value="xsd asc")
@JsonIgnore
private Set<Item> items = new HashSet<Item>();
How can I bind hibernate session in order to LAZELY load my object inside @Async
context?
EDIT
Here is my trancactionManager / entityManager configuration
<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"/>
I had the same problem, spent few days trying to find a solution, finally got a solution. I would like to share the details I found for those who might have the same issue.
1st - Your @Async
-annotated method should be declared in a separate bean rather than the @Controller
- or @RestController
-annotated bean.
2nd - You certainly need to declare the method @Transactional
which is called from within @Async
declared method. However, the very first method called from @Async
method has to be defined @Transactional
. I had the @Transactional
method in second or third level in the method execution stack therefore the problem was not solved and I spent two days trying to figure out what was going on.
So the best thing to do is
@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);
}
}