[英]Spring 4 + Hibernate 4 transaction management error
I'm kinda new with the Spring's transaction management. 我对Spring的事务管理有点陌生。 I think I'm missing some configuration but I can't deal with it. 我想我缺少一些配置,但是我无法处理。
The error is that I'm getting failed to lazily initialize a collection of role
exception. 错误是我failed to lazily initialize a collection of role
异常failed to lazily initialize a collection of role
。
I'm using Spring Data for my DAO. 我将Spring Data用于我的DAO。 Also, I know about setting the fetchType to Eager but this is what I'm trying to avoid. 另外,我知道将fetchType设置为Eager,但这是我要避免的事情。
DAO: DAO:
public interface CourseDao extends CrudRepository<CourseEntity, Long> {
CourseEntity findByName(String name);
}
Service: 服务:
@Service
public class CourseMaterialSearchService {
private final CourseDao courseDao;
private final CourseMaterialEntityTransformer courseMaterialEntityTransformer;
@Autowired
public CourseMaterialSearchService(CourseDao courseDao, CourseMaterialEntityTransformer courseMaterialEntityTransformer) {
super();
this.courseDao = courseDao;
this.courseMaterialEntityTransformer = courseMaterialEntityTransformer;
}
@Transactional
public List<CourseMaterial> findMaterialsFor(final Long courseId) {
final CourseEntity entity = courseDao.findOne(courseId);
final List<CourseMaterialEntity> materials = entity.getCourseMaterialEntityList();
return courseMaterialEntityTransformer.transformEntities(materials);
}
}
And my application-context.xml is (of course this is just the relevant part): 我的application-context.xml是(当然,这只是相关部分):
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="example" />
<property name="dataSource" ref="exampleDataSource" />
<property name="packagesToScan" value="com.example.example.**.repository" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
When I call the findMaterialsFor
method in CourseMaterialSearchService
, the exception occurs. 当我在CourseMaterialSearchService
调用findMaterialsFor
方法时,会发生异常。 How should I solve this? 我该如何解决?
Any suggestions will be appreciated. 任何建议将不胜感激。
Thank you guys. 感谢大伙们。
Stacktrace: 堆栈跟踪:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.example.course.repository.domain.CourseEntity.courseMaterialEntityList, could not initialize proxy - no Session
org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:575)
org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:214)
org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:554)
org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:142)
org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:294)
com.example.example.course.service.transform.CourseMaterialEntityTransformer.transformEntities(CourseMaterialEntityTransformer.java:15)
com.example.example.course.service.CourseMaterialSearchService.findMaterialsFor(CourseMaterialSearchService.java:29)
CourseMaterialEntityTransformer: CourseMaterialEntityTransformer:
public class CourseMaterialEntityTransformer {
public List<CourseMaterial> transformEntities(final Iterable<CourseMaterialEntity> entities) {
final List<CourseMaterial> result = new ArrayList<>();
for (final CourseMaterialEntity entity : entities) {
result.add(transformEntity(entity));
}
return result;
}
public CourseMaterial transformEntity(final CourseMaterialEntity entity) {
final CourseMaterial result = new CourseMaterial();
result.setId(entity.getId());
result.setName(entity.getName());
result.setCourseName(entity.getCourseEntity().getName());
result.setCurrentFileName(entity.getCurrentFileName());
result.setOriginalFileName(entity.getOriginalFileName());
result.setCategory(entity.getMaterialCategoryEntity().getName());
result.setUploader(entity.getUserEntity().getName());
return result;
}
}
com.example.example.course.service.transform.CourseMaterialEntityTransformer.transformEntities(CourseMaterialEntityTransformer.java:15)
: com.example.example.course.service.transform.CourseMaterialEntityTransformer.transformEntities(CourseMaterialEntityTransformer.java:15)
:
for (final CourseMaterialEntity entity : entities) {
CourseEntity: CourseEntity:
@Entity(name = "courses")
public class CourseEntity {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "courseEntity")
private List<CourseMaterialEntity> courseMaterialEntityList;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public List<CourseMaterialEntity> getCourseMaterialEntityList() {
return courseMaterialEntityList;
}
public void setCourseMaterialEntityList(final List<CourseMaterialEntity> courseMaterialEntityList) {
this.courseMaterialEntityList = courseMaterialEntityList;
}
}
Spring applies transactions using AOP. Spring使用AOP应用事务。 AOP on beans is only applied to beans in the same application context. bean上的AOP仅适用于同一应用程序上下文中的bean。
You have a <tx:annotation-driven />
in your application-context.xml
, which is loaded by the ContextLoaderListener
. 您的application-context.xml
有一个<tx:annotation-driven />
,由ContextLoaderListener
加载。 This file contains a <context:component-scan />
which detects the @Service
. 该文件包含一个<context:component-scan />
,它检测@Service
。
However if you also have the same <context:component-scan />
in the file loaded by the DispatcherServlet
, or at least if the same @Service
is detected it results in another instance of this service which will not have AOP applied. 但是,如果在DispatcherServlet
加载的文件中还具有相同的<context:component-scan />
,或者至少如果检测到相同的@Service
,则会导致该服务的另一个实例没有应用AOP。
As a general rule-of-thumb you want to load everything BUT @Controllers
in your ContextLoaderListener
and only web related things (view resolvers, @Controllers
) in your DispatcherServlet
. 作为一般的经验法则,您希望在ContextLoaderListener
加载所有的BUT @Controllers
,而仅在DispatcherServlet
加载与Web相关的东西(视图解析器, @Controllers
)。
See context depended scan-component filter . 请参阅上下文相关的扫描组件过滤器 。
You are trying to access a lazy-loaded attribute outside of a transaction. 您正在尝试访问事务外部的延迟加载属性。 The object then has no connection with the EntityManager (database), since it's outside of a transaction, and thus cannot go fetch the lazy-loaded attribute. 然后,该对象与EntityManager(数据库)没有任何关系,因为它在事务外部,因此无法获取延迟加载的属性。
By default Collections are lazy-loaded . 默认情况下,集合是延迟加载的。 You can replace below your code : 您可以在代码下方替换:
@OneToMany(mappedBy = "courseEntity")
private List<CourseMaterialEntity> courseMaterialEntityList;
With : 与:
@OneToMany(fetch = FetchType.EAGER, mappedBy = "courseEntity", cascade = CascadeType.ALL)
private List<CourseMaterialEntity> courseMaterialEntityList;
You need to change @OneToMany(mappedBy = "courseEntity")
to @OneToMany(mappedBy = "courseEntity", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
您需要将@OneToMany(mappedBy = "courseEntity")
更改为@OneToMany(mappedBy = "courseEntity", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
The default fetch type is lazy so when you try and access the collection it hasn't been loaded from the database. 默认的获取类型是惰性的,因此当您尝试访问集合时,尚未从数据库中加载它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.