Hibernate 中对象 set 属性时自动保存入库的问题

背景

在某个项目中,通过 Heibernate 查出某个对象后,对属性进行了 set 操作,并没有进行 update 操作,但是在数据中自动保存了。

显然这并不是我想要的,我只是想 set 一下属性,在前端显示,并不想入库,但是 Heibernate 自动保存了。

1
2
3
4
5
// 查出对象,原来的 name 为 user1
User user = userManager.findOne(1);
// 将userName 设置为 user2 ,用于前端显示,并没有保存
user.setName("user2");
// heibernate 自动进行了保存,库中的 name 为 user2

原因

Heibernate 中对象分为以下几种状态:

瞬时状态:

实际上就是 new 了一个普通的 JavaBean 对象。

托管状态:

  • 当瞬时对象调用了管理器的 persist() 后,即可将一般的 JavaBean 做为了持久 Bean,该 Bean 的任何属性改动都会牵涉到数据库记录的改动。

  • 一旦该记录 flush 到数据库之后,并且事务提交了,那么此对象不在持久化上下文中,即:变为了游离(没人管的孩子)状态了。在游离状态的时候调用更新、刷新方法后,游离状态对象就变为了在持久化上下文的托管状态了。

  • 通过管理器的 find 方法,将实体从数据库查询出来后,该实体也就变为了托管形态。

持久化状态:

当处在托管状态的实体 Bean 被管理器 flush 了,那么就在极短暂的时间进入了持久化状态,事务提交之后,立刻变为了游离状态。您可以把持久化状态当做实实在在的数据库记录。

游离状态:

游离状态就是提交到数据库后,事务 commit 后实体的状态,因为事务已经提交了,此时实体的属性任你如何改变,也不会同步到数据库,因为游离是没人管的孩子,不在持久化上下文中。

销毁对象:

一般要删除一个持久化对象的时候都是先 find 出来,之后调用 remove 方法删之,此时这个对象就是销毁对象,实际上就是瞬时对象的另一种形态罢了。

根据上面的各种状态,可以得知。通过 find 查出某个对象后,就进入了托管状态,此时对对象的任何属性改动,都会自动保存到数据库中。

解决方法

  1. 通过 new 一个新的对象出来进行操作,这个新的对象是瞬时状态,不会自动保存。
1
2
3
4
5
6
7
8
// 查出对象
User user = userManager.findOne(1);
// new 一个新的对象
User newUser = new User();
// 将需要用到的属性赋予新的对象,对于前端显示
newUser.setAge(user.getAge());
newUser.setName("user2");
// 不要对 newUser 进行保存即可。也不要改变 user 的属性
  1. 在 find 查出的对象 set 属性前,将其状态改为游离状态。这样的话会用到 session 的几个方法:close、clear、evict。

    • close 方法:关闭 session 这样这个对象肯定是游离态了,因为 session 已经关闭了,但是往往我们实际的开发过程中,session 在后面是要用的到的,所以这个方法可行,但是不一定用得上,分清具体的情况。

    • clear 方法:将 session 中的所有的对象全部清除出缓存,这个方式有点劳师动众,不过 session 清除了全部的对象之后自然就会变为游离态了,这样做不是很好吧我感觉。

    • evict 方法:将某一个对象清除出缓存 session,这个方法是很好的实现方式,推荐使用。调用的时候是这样的,session.evict(Object obj) 这样就可以了。

    这边推荐使用 evict 方法,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @PersistenceContext
    private EntityManager entityManager;

    ......

    // 获取 session
    HibernateEntityManager hEntityManager = (HibernateEntityManager)entityManager;
    Session session = hEntityManager.getSession();
    // 查出对象,原来的 name 为 user1
    User user = userManager.findOne(1);
    // 将对象清除出缓存 session,这样后面对属性的改变就不会自动保存
    session.evict(user);
    // 将userName 设置为 user2 ,用于前端显示
    user.setName("user2");
    // 由于清除出了 session,heibernate 不会对这个对象进行保存,库中的 name 仍为 user1

总结

通过 Hibernate 查询出来的对象,改变其属性后,Hibernate 会对其自动保存入库。

如果不需要使用这个功能,可以通过 new 一个新的对象来进行操作,或者将对象改成游离态。