尽管在@Transactional中并且没有分离的实体,但仍获取LazyInitializationException

时间:2015-07-13 20:04:31

标签: spring hibernate lazy-loading transactional lazy-initialization

我正在使用Spring 3.2.11.RELEASE,JPA 2.1,Hibernate 4.3.6.Final和MySQL 5.5.37。我在JUnit测试中得到以下错误

testSendValidAssignment(org.mainco.subco.thirdParty.service.ThirdPartyServiceIT)  Time elapsed: 13.366 sec  <<< ERROR!
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:575)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:214)
    at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:155)
    at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:278)
    at org.mainco.subco.ThirdParty.service.ThirdPartyServiceIT.testSendValidAssignment(ThirdPartyServiceIT.java:102)

但是,我不知道这条错误消息应该归咎于哪一行。在我的方法中我有

@Service
@Transactional
public class MyServiceImpl implements MyService
{
....

@Override
public void sendAssignment(final String assignmentId)
{
    final Assignment assignment = m_lessonPlanDao.getAssignment(assignmentId);
    if (processData(assignment))
    {
        // Gather the students who have been assigned this assignment
        final List<Classroom> classes = lessonPlan.getClassrooms();
        System.out.println("got " + classes.size() + " classes.");
        // Send one request for each class assignment
        for (final Classroom classroom : classes)
        {
            final List<User> classStudents = m_classroomSvc.findClassStudents(classroom.getId());
            System.out.println("got " + classStudents.size() + " students.");

并且两个System.out行都打印数字。在方法中没有其他对这个延迟加载的集合的引用,所以我不知道还有什么可以检查我的数据。任何建议都表示赞赏。

1 个答案:

答案 0 :(得分:1)

Your service method is transactional, but your entire test method is not. The size checking of the element is performed in an assertion statement in your JUnit test and is done outside of the transaction scope of the service, so it causes lazy initialization exception. There are three ways you can go

  • you can try call size() method inside your dao method to make outside call of the size() method safe.
  • you can force the user of your method to open transaction(document which objects are detached and make it a contract/or use infamous open session in view pattern)
  • you can create a DTO layer and return size as a part of DTO

all three solutions have pros and cons:

the first solution will look weird for the supporters of the code when they'll find that you call a getter for no obvious reson - it needs commenting.

the second makes life harder to the users of the interface

the third violates DRY principle

p.s. you can also disable lazy initialization of course