【问题标题】:Hibernate LazyInitializationException with Spring MVC使用 Spring MVC 的 Hibernate LazyInitializationException
【发布时间】:2014-07-11 15:37:11
【问题描述】:

问题

使用 Kolorbot 的 Spring MVC 4 Quickstart archetype 我创建了一个实体、一个存储库和一个服务类,它可以通过控制器方法访问。在我的实体中,我通过延迟获取与另一个实体建立了多对一关系。

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

访问控制器方法中指向相关实体的实体属性我收到“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) {
        ...
    }
}

以及我尝试通过实体实例访问的相关实体:

@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 似乎工作并且在访问我的控制器和视图中的实体属性时会话仍然打开。 Hibernate Community Documentation 中详细记录了惰性获取机制和避免 LazyInitializationException 的可能解决方案。

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

【问题讨论】:

  • 你能贴出相关实体类的代码吗?
  • 感谢您的关注。我刚刚添加了一个源代码部分。
  • 你试过公开无参数构造函数吗?
  • ...它不应该只是一个想法。

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


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 2017-02-23
    • 2012-11-19
    • 2012-12-28
    • 2017-11-21
    相关资源
    最近更新 更多