JPA(Hibernate)实体状态

一句话总结:通过JPA(Hibernate)对实体对象进行增删改查时,JPA(Hibernate)要维护即存储对象在Session即Hibernate一级缓存中,同时还要维护其状态,具体状态变化如下图所示:

通常我们无需关心JPA(Hibernate)的实体状态,但是碰到一些问题时需要对实体状态有所了解。

问题一:Cannot remove detached entity异常

通常我们会通过Spring配置自动事务,此时若在北向调用ServiceA查询出一个对象,这里查询也会有事务提交,那么这个对象其实处于detached状态,北向再调用ServiceB传递此对象进行删除,就会报错提示cannot remove detached entity异常。还有一个场景是批量提交场景,网上的示例通常在批量提交后会调用em.clear来释放内存,此时该对象也处于detached状态,再remove同样会报错。

northbound method() {
    Object obj = service.findObject(id);
    service.removeObject(obj);  
}

service findObject method() {
    // Spring事务,自动提交commit
    return object;
}

service removeObject method(Object obj) {
    // 这里会报错
    JpaEntityManager.remove(obj);
}

 问题二:了解实体状态后,我们知道事务自动提交后再操作对象会有detach异常,那么设计架构时就要确保每次REST请求时使用不同的Hibernate Session,若并发多次请求使用同一Session里缓存的对象,就得手动去维护对象的状态才能防止出现detach异常。

通常我们还会使用Spring IOC,注入单例对象,这会让我们觉得Hibernate Session是同一个,如下:

class Northbound {
    @Autowired
    Service service;
}

class Service {
    @PersistenceContext
    EntityManager em;
}

但其实Spring已经帮我们隐含处理了,这里有点绕,Northbound注入Service时是单例的,每次调用的是同一个service对象。但Service注入EntityManager时,却会根据每个线程即每次REST请求注入新的EntityManager(其下封装Hibernate Session)。Spring会针对PersistenceContext注解做特殊处理,将EntityManager(Session)与线程绑定,存储在ThreadLocal中。在OSGI环境中,还会使用OSGI Coordinate机制。所以用断点调试去查看,每次请求的EntityManager对象都是不同的。

引用:

https://docs.oracle.com/cd/E16439_01/doc.1013/e13981/undejbs003.htm

https://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_overview_em_lifecycle.html

https://stackoverflow.com/questions/42074270/should-there-be-an-entitymanager-per-thread-in-spring-hibernate

原文地址:https://www.cnblogs.com/yuzhengzhong/p/9785506.html