[英]Hibernate LazyInitializationException with Spring MVC
Using Kolorbot's Spring MVC 4 Quickstart archetype I created an entity, a repository and a service class which is accessed by a controller method. 使用Kolorbot的Spring MVC 4快速入门原型,我创建了一个实体,一个存储库和一个服务类,可通过控制器方法访问该类。 In my entity I have a ManyToOne relationship to another entity by fetching it lazily. 在我的实体中,我通过延迟获取与另一个实体建立了ManyToOne关系。
In my controller method I invoke the service's method which itself invokes a method on the repository which eventually fetches the needed data and passes it on through the layers. 在我的控制器方法中,我调用服务的方法,该方法本身又在存储库中调用一个方法,该方法最终获取所需的数据并将其传递到各个层。
Accessing the entity's attributes in the controller's method that point to the related entity I receive a "LazyInitializationException". 访问控制器方法中指向相关实体的实体属性时,我收到“ LazyInitializationException”。
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
While reading up on the problem I stumbled upon solutions describing the usage of the @Transactional annotation which does not help or the inclusion of the OpenEntityManagerInViewFilter that is supposed to force the session to stay open until the controller's method are processed. 在阅读问题时,我偶然发现了解决方案,这些解决方案描述了@Transactional批注的用法,该方法无济于事,或者包含OpenEntityManagerInViewFilter,后者应强制会话保持打开状态,直到处理控制器的方法为止。
The inclusion of the OpenEntityManagerInViewFilter: 包含OpenEntityManagerInViewFilter:
@Override
protected Filter[] getServletFilters() {
OpenEntityManagerInViewFilter openEntityManagerInViewFilter = new OpenEntityManagerInViewFilter();
openEntityManagerInViewFilter.setBeanName("openEntityManagerInViewFilter");
...
return new Filter[] {openEntityManagerInViewFilter, characterEncodingFilter, securityFilterChain};
}
The usage of the filter leads to the following exception: 使用过滤器会导致以下异常:
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 is the object I try to access through the original entity I am interested in. RelatedEntity是我尝试通过我感兴趣的原始实体访问的对象。
By setting the lazy fetching mechanism to false I receive the following exception (thrown in my controller's method):
通过将延迟获取机制设置为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)
By removing the try/catch block in the repository's findEntityById()
method (see below) and adding the annotation @Transactional(noRollbackFor = PersistenceException.class)
I was able to enable eager loading. 通过删除存储库的findEntityById()
方法中的try / catch块(请参见下文)并添加注释@Transactional(noRollbackFor = PersistenceException.class)
我能够启用急切加载。 But this is just a temporary solution. 但这只是一个临时解决方案。 Nevertheless I'd like to use lazy fetching. 尽管如此,我还是想使用延迟获取。
The entity class that is returned by the service method: 服务方法返回的实体类:
@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) {
...
}
}
And the RelatedEntity that I try to access through an instance of Entity: 以及我尝试通过Entity实例访问的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) {
...
}
}
Finally, the repository and the service class: 最后,存储库和服务类:
@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);
}
...
}
I am not exactly sure what happened but suddenly the OpenEntityManagerInViewFilter seems to work and the session's still open when accessing the entity's properties in my controller and view. 我不确定发生了什么,但突然在访问控制器和视图中的实体属性时,OpenEntityManagerInViewFilter似乎可以正常工作,并且会话仍处于打开状态。 A further insight into the lazy fetching mechanism and the possible solutions to avoid the LazyInitializationException is well documented in the Hibernate Community Documentation . Hibernate社区文档中详细记录了对延迟获取机制和避免LazyInitializationException的可能解决方案的进一步了解。
The initialization of the lazily fetched object through Hibernate.initialize()
in the service layer is a valid alternative but generates too much unnecessary overhead. 通过服务层中的Hibernate.initialize()
初始化延迟获取的对象是一种有效的替代方法,但会产生过多的不必要开销。
In order to assure that your lazy-loading request is bound to a session, you should initialize it in your service layer: 为了确保您的延迟加载请求已绑定到会话,应在服务层中将其初始化:
@Override
public List<Entity> findEntitiesById(Long id) {
List<Entity> entityList = entityRepository.findAgreementById(id);
for (Entity e: entityList){
Hibernate.initialize(e.getRelatedEntity());
}
return entityList;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.