Spring 4 + Hibernate 4 事务管理错误
Spring 4 + Hibernate 4 transaction management error
我对 Spring 的事务管理有点陌生。我想我缺少一些配置,但我无法处理它。
错误是我收到 failed to lazily initialize a collection of role
异常。
我正在为我的 DAO 使用 Spring 数据。另外,我知道将 fetchType 设置为 Eager,但这是我要避免的。
DAO:
public interface CourseDao extends CrudRepository<CourseEntity, Long> {
CourseEntity findByName(String name);
}
服务:
@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);
}
}
而我的申请-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>
当我调用CourseMaterialSearchService
中的findMaterialsFor
方法时,出现了异常。我该如何解决?
如有任何建议,我们将不胜感激。
谢谢大家。
堆栈跟踪:
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:
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)
:
for (final CourseMaterialEntity entity : entities) {
课程实体:
@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;
}
}
您正在尝试访问事务外的惰性加载属性。然后该对象与 EntityManager(数据库)没有任何联系,因为它在事务之外,因此无法获取延迟加载的属性。
默认情况下,集合是延迟加载的。您可以替换下面的代码:
@OneToMany(mappedBy = "courseEntity")
private List<CourseMaterialEntity> courseMaterialEntityList;
与:
@OneToMany(fetch = FetchType.EAGER, mappedBy = "courseEntity", cascade = CascadeType.ALL)
private List<CourseMaterialEntity> courseMaterialEntityList;
您需要将 @OneToMany(mappedBy = "courseEntity")
更改为 @OneToMany(mappedBy = "courseEntity", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
默认的获取类型是惰性的,因此当您尝试访问该集合时,它尚未从数据库中加载。
Spring 使用 AOP 应用事务。 beans 上的 AOP 仅适用于同一应用程序上下文中的 beans。
您的 application-context.xml
中有一个 <tx:annotation-driven />
,由 ContextLoaderListener
加载。此文件包含一个 <context:component-scan />
,它检测 @Service
.
但是,如果您在 DispatcherServlet
加载的文件中也有相同的 <context:component-scan />
,或者至少如果检测到相同的 @Service
,则会导致此服务的另一个实例不会应用 AOP。
作为一般的经验法则,您希望在 ContextLoaderListener
中加载除 @Controllers
之外的所有内容,并且只在 [=] 中加载与 Web 相关的内容(查看解析器,@Controllers
) 16=]。
参见 context depended scan-component filter。
我对 Spring 的事务管理有点陌生。我想我缺少一些配置,但我无法处理它。
错误是我收到 failed to lazily initialize a collection of role
异常。
我正在为我的 DAO 使用 Spring 数据。另外,我知道将 fetchType 设置为 Eager,但这是我要避免的。
DAO:
public interface CourseDao extends CrudRepository<CourseEntity, Long> {
CourseEntity findByName(String name);
}
服务:
@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);
}
}
而我的申请-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>
当我调用CourseMaterialSearchService
中的findMaterialsFor
方法时,出现了异常。我该如何解决?
如有任何建议,我们将不胜感激。
谢谢大家。
堆栈跟踪:
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:
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)
:
for (final CourseMaterialEntity entity : entities) {
课程实体:
@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;
}
}
您正在尝试访问事务外的惰性加载属性。然后该对象与 EntityManager(数据库)没有任何联系,因为它在事务之外,因此无法获取延迟加载的属性。
默认情况下,集合是延迟加载的。您可以替换下面的代码:
@OneToMany(mappedBy = "courseEntity")
private List<CourseMaterialEntity> courseMaterialEntityList;
与:
@OneToMany(fetch = FetchType.EAGER, mappedBy = "courseEntity", cascade = CascadeType.ALL)
private List<CourseMaterialEntity> courseMaterialEntityList;
您需要将 @OneToMany(mappedBy = "courseEntity")
更改为 @OneToMany(mappedBy = "courseEntity", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
默认的获取类型是惰性的,因此当您尝试访问该集合时,它尚未从数据库中加载。
Spring 使用 AOP 应用事务。 beans 上的 AOP 仅适用于同一应用程序上下文中的 beans。
您的 application-context.xml
中有一个 <tx:annotation-driven />
,由 ContextLoaderListener
加载。此文件包含一个 <context:component-scan />
,它检测 @Service
.
但是,如果您在 DispatcherServlet
加载的文件中也有相同的 <context:component-scan />
,或者至少如果检测到相同的 @Service
,则会导致此服务的另一个实例不会应用 AOP。
作为一般的经验法则,您希望在 ContextLoaderListener
中加载除 @Controllers
之外的所有内容,并且只在 [=] 中加载与 Web 相关的内容(查看解析器,@Controllers
) 16=]。
参见 context depended scan-component filter。