0%

JPA hibernate的持久化生命状态

开篇

某日写程序时,使用jpa操作数据库,当我使用findAll()方法查处一个List的对象后,给对这个list的实体进行了一些操作,并没有调用update 或者 saveOrUpdate方法,更改后的数据却神奇的保存到数据库里面去了😕😕😕我也在service层添加事物了啊。后面找了资料才发现是jpa是对hibernate的封装,底层是hibernate,这是hibernate的持久状态搞的鬼。

hibernate 的三种状态

  1. 瞬时状态 (Transient)
    当我们通过Java的new关键字来生成一个实体对象时,这时这个实体对象就处于自由状态,如下:
    People people=new People(“xs”,20);
    此时people只是通过JVM获得了一块内存空间,还并没有通过Session对象的save()方法保存进数据库,因此也就还没有纳入Hibernate的缓存管理中,也就是说customer对象现在还自由的游荡于Hibernate缓存管理之外。所以我们可以看出自由对象最大的特点就是,在数据库中不存在一条与它对应的记录。
    瞬时对象特点:
    不和 Session 实例关联
    在数据库中没有和瞬时对象关联的记录
  2. 持久状态 (Persistent)
    持久化对象就是已经被保存进数据库的实体对象,并且这个实体对象现在还处于Hibernate的缓存管理之中。这是对该实体对象的任何修改,都会在清理缓存时同步到数据库中。如下所示:
1
2
3
4
5
6
People people=new People(“xs”,20);
tx=session.beginTransaction();
session.save(people);
customer=(Customer)session.load(Customer.class,”1”);
customer.setAge(28);
tx.commit();

这时我们并没有显示调用session.update()方法来保存更新,但是对实体对象的修改还是会同步更新到数据库中,因为此时customer对象通过save方法保存进数据库后,已经是持久化对象了,然后通过load方法再次加载它,它仍然是持久化对象,所以它还处于Hibernate缓存的管理之中,这时当执行tx.commit()方法时,Hibernate会自动清理缓存,并且自动将持久化对象的属性变化同步到到数据库中。
持久的实例在数据库中有对应的记录,并拥有一个持久化标识 (identifier).持久对象总是与 SessionTransaction 相关联,在一个 Session 中,对持久对象的改变不会马上对数据库进行变更,而必须在Transaction终止,也就是执行 commit() 之后,才在数据库中真正运行 SQL 进行变更,持久对象的状态才会与数据库进行同步。在同步之前的持久对象称为脏 (dirty) 对象。
瞬时对象转为持久对象:
通过 Sessionsave()saveOrUpdate() 方法把一个瞬时对象与数据库相关联,这个瞬时对象就成为持久化对象。
使用 fine(),get(),load()iterater() 待方法查询到的数据对象,将成为持久化对象。
持久化对象的特点:
和 Session 实例关联
在数据库中有和持久对象关联的记录
3. 脱管状态 (Detached)
当一个持久化对象,脱离开Hibernate的缓存管理后,它就处于游离状态,游离对象和自由对象的最大区别在于,游离对象在数据库中可能还存在一条与它对应的记录,只是现在这个游离对象脱离了Hibernate的缓存管理,而自由对象不会在数据库中出现与它对应的数据记录。如下所示:

1
2
3
4
5
6
7
People people=new People(“xs”,20);
tx=session.beginTransaction();
session.save(people);
people=(People)session.load(People.class,”1”);
people.setAge(28);
tx.commit();
session.close();

当session关闭后,customer对象就不处于Hibernate的缓存管理之中了,但是此时在数据库中还存在一条与customer对象对应的数据记录,所以此时customer对象处于游离态
与持久对象关联的 Session 被关闭后,对象就变为脱管对象。对脱管对象的引用依然有效,对象可继续被修改。
脱管对象特点:
本质上和瞬时对象相同
只是比瞬时对象多了一个数据库记录标识值 id.
持久对象转为脱管对象:
当执行 close() clear(),evict() 之后,持久对象会变为脱管对象。
瞬时对象转为持久对象:
通过 Sessionupdate(),saveOrUpdate() lock() 等方法,把脱管对象变为持久对象。

JPA 实体生命周期有四种状态

New:瞬时对象,尚未有id,还未和Persistence Context建立关联的对象。
Managed:持久化受管对象,有id值,已经和Persistence Context建立了关联的对象。
Datached:游离态离线对象,有id值,但没有和Persistence Context建立关联的对象。
Removed:删除的对象,有id值,尚且和Persistence Context有关联,但是已经准备好从数据库中删除。
Managed状态下的数据保存,更新以及删除数据下的Removed状态,数据都不会立即更新到数据库,只有当你事务提交或者em.flush(),才会立即更新到数据库。
Datached的状态,可以调用em.merge()方法,这个方法会根据实体类的id来更新数据库数据,这时实体类变成了Managed状态。
四种状态总结:

状态名 作为java对象存在 在实体管理器中存在 在数据库存在

New yes no no

Managed yes yes yes

Detached no no no

Removed yes yes no

解决办法

  1. 注入EntityManager
1
2
@PersistenceContext
EntityManager entityManager;
  1. 临时赋值后,detch对象
1
entityManager.detach(xxx);

eg:

1
2
3
4
5
6
7
List<Video> living = videos.getContent().stream().filter(x->x.getIsLive()!=null).filter(x->x.getIsLive()==2).filter(x->x.getLiveTime().compareTo(new Date())>0).collect(Collectors.toList());
living.forEach(e->{
e.setHd("");
e.setSd("");
e.setUltraClear("");
entityManager.detach(e);
});

detach()方法

1
2
3
4
5
Method
javax.persistence.EntityManager
void detach(
Object entity
)

Remove the given entity from the persistence context, causing a managed entity to become detached. Unflushed changes made to the entity if any (including removal of the entity), will not be synchronized to the database. Entities which previously referenced the detached entity will continue to reference it.
Parameters:
entity - entity instance
Throws:
IllegalArgumentException - if the instance is not an entity
Since:
JPA 2.0

参考链接

欣赏此文?求鼓励,求支持!