Spring MVC的Hibernate LazyInitializationException

时间:2014-05-22 14:20:28

标签: spring hibernate exception spring-mvc lazy-initialization

问题

使用Kolorbot' s Spring MVC 4 Quickstart archetype我创建了一个实体,一个存储库和一个服务类,可以通过控制器方法访问。在我的实体中,我通过懒惰的方式获取与另一个实体的ManyToOne关系。

在我的控制器方法中,我调用服务的方法,该方法本身调用存储库上的方法,该方法最终获取所需的数据并通过层传递它。

访问控制器方法中指向相关实体的实体属性,我收到" LazyInitializationException"。

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

研究和尝试解决问题

在阅读问题的同时,我偶然发现了描述@Transactional注释使用的解决方案,这些解决方案没有帮助,或者包含OpenEntityManagerInViewFilter,它应该强制会话保持打开状态,直到控制器的方法为止。处理。

包含OpenEntityManagerInViewFilter:

@Override
protected Filter[] getServletFilters() {
    OpenEntityManagerInViewFilter openEntityManagerInViewFilter = new OpenEntityManagerInViewFilter();
    openEntityManagerInViewFilter.setBeanName("openEntityManagerInViewFilter");

    ...

    return new Filter[] {openEntityManagerInViewFilter, characterEncodingFilter, securityFilterChain};
}

过滤器的使用会导致以下异常:

org.hibernate.HibernateException: Javassist Enhancement failed: com.example.package.RelatedEntity
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.getProxy(JavassistLazyInitializer.java:143)
at org.hibernate.proxy.pojo.javassist.JavassistProxyFactory.getProxy(JavassistProxyFactory.java:73)

RelatedEntity是我尝试通过我感兴趣的原始实体访问的对象。

<击> 通过将延迟提取机制设置为false,我收到以下异常(在我的控制器方法中抛出):

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly] with root cause
javax.persistence.RollbackException: Transaction marked as rollbackOnly
    at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:72)

<击>

通过删除存储库的findEntityById()方法中的try / catch块(见下文)并添加注释@Transactional(noRollbackFor = PersistenceException.class),我能够启用预先加载。但这只是一个临时解决方案。不过我还是喜欢使用延迟抓取。


源代码部分

服务方法返回的实体类:

@Entity
@Table(name = "Entity")
@NamedQuery(
        name = Entity.FIND_BY_ID,
        query = "select e from Entity e where e.id = :id"
)
public class Entity {

    public static final String FIND_BY_ID = "Entity.findById";

    @Id
    @Column(name = ID)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Getter
    private Long id;

    ...

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name=RelatedEntity.ID)
    @Getter
    private RelatedEntity relatedEntity;

    ...

    protected Entity() {}

    public Entity(String param1) {
        ...
    }
}

我尝试通过实体实例访问的RelatedEntity:

@Entity
@Table(name = "RELATED_ENTITY")
@NamedQuery(name = RelatedEntity.FIND_BY_ID, query = "select re from RelatedEntity re where re.id = :id")
public class RelatedEntity {

    public static final String FIND_BY_ID = "RelatedEntity.findById";
    public static final String ID = "RE_ID";

    @Id
    @Column(name=ID)
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    ...

    @OneToMany(mappedBy="relatedEntity")
    private List<Entity> entities;

    ...

    protected RelatedEntity() {}

    public RelatedEntity(String param1) {
        ...
    }

}

最后,存储库和服务类:

@Repository
public class EntityRepository {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional(noRollbackFor = PersistenceException.class)
    public List<Entity> findEntityById(Long id) {
        return entityManager.createNamedQuery(Entity.FIND_BY_ID, Entity.class)
                .setParameter("id", id)
                .getResultList();
    }

}

@Service
public class DefaultEntityService implements EntityService {

    @Autowired
    EntityRepository entityRepository;

    ...

    @Override
    public List<Entity> findEntitiesById(Long id) {
        return entityRepository.findAgreementById(id);
    }

    ...
}

解决方案

我不确定发生了什么但突然OpenEntityManagerInViewFilter似乎工作,并且在我的控制器和视图中访问实体的属性时会话仍然打开。进一步深入了解延迟提取机制以及避免LazyInitializationException的可能解决方案已在Hibernate Community Documentation中详细记录。

在服务层中通过Hibernate.initialize()初始化延迟取出的对象是一种有效的替代方法,但会产生太多不必要的开销。

1 个答案:

答案 0 :(得分:1)

为了确保您的延迟加载请求绑定到会话,您应该在服务层初始化它:

@Override
public List<Entity> findEntitiesById(Long id) {
    List<Entity> entityList = entityRepository.findAgreementById(id);
    for (Entity e: entityList){
        Hibernate.initialize(e.getRelatedEntity());
    }
    return entityList; 
}