Hibernate实践—持久态对象的变化与入库机制

数据库 其他数据库
在持久态下,任何对 userInfo​ 和 userPassword​ 对象的属性的修改都将被监测到,并在适当的时候同步到数据库中。在测试方法的最后,我们对 userPassword​ 的 password​ 属性和 userInfo​ 的 user_name​ 属性进行了修改。

持久化后修改属性,会发生什么

@Test
    public void demo3(){
        UserInfo userInfo = new UserInfo();
        userInfo.user_name = "demo123";
        userInfo.user_sex = 1;
        userInfo.user_age = 18;
        UserPassword userPassword = new UserPassword();
        userPassword.password = "demo-password";
        userInfo.userPassword = userPassword;
        userPassword.UserPassword_userInfo = userInfo;
        BaseEntityUtils.save(userInfo);
        System.out.println(userInfo.user_name);
        userInfo.user_name = "demo1234";
        userPassword.password = "demo1234-password";
        UserInfo new1 = BaseEntityUtils.findById(userInfo.id,UserInfo.class);
        System.out.println(new1.user_name);
    }

在提供的代码中,在执行 BaseEntityUtils.saveAndFlush(userInfo) 之后,userInfo 和 userPassword 对象处于持久态(Persistent State)。

userInfo 对象的状态

  • 由于使用了 BaseEntityUtils.saveAndFlush(userInfo) 方法,userInfo 对象被保存到数据库,并且通过 flush 操作将其状态同步到数据库中。
  • 因此,userInfo 对象现在处于持久态,它受到 Hibernate Session 的管理。

userPassword 对象的状态

  • userPassword 对象与 userInfo 之间存在一对一的关联关系,由于级联关系的存在,userPassword 对象也被保存到数据库中。
  • userPassword 对象的状态也是持久态,它同样受到 Hibernate Session 的管理。

在持久态下,任何对 userInfo 和 userPassword 对象的属性的修改都将被监测到,并在适当的时候同步到数据库中。在测试方法的最后,我们对 userPassword 的 password 属性和 userInfo 的 user_name 属性进行了修改。这些修改将在 Hibernate Session 中被跟踪,但由于在测试方法中并没有进行事务的提交或刷新,这些变化可能尚未同步到数据库。如果我们想要确保变化及时更新到数据库中,我们可以在测试方法的最后添加 BaseEntityUtils.flush() 或者使用 Spring 的 @Transactional 注解,以确保事务的正确提交和刷新。这样,持久态下的对象变化将会及时同步到数据库。

什么时候会刷新到数据库

在 Hibernate 中,对于持久态的实体对象,属性的修改会被监测到,并且会在以下情况下被同步到数据库

事务提交时

当事务被提交时,Hibernate 会检查事务中所有持久态对象的变化,并将这些变化同步到数据库。这是最常见的触发时机。

显式调用 flush() 方法时

我们可以显式调用 Hibernate Session 的 flush() 方法,强制将所有挂起的 SQL 语句发送到数据库。这样可以在事务未提交的情况下将变化同步到数据库。

在查询时自动执行 flush

Hibernate 在执行一些查询操作(例如执行查询语句之前)时,会自动执行 flush 操作,以确保最新的数据被加载。这种情况通常涉及到缓存和查询的一致性。所以,当我们在持久态的对象上进行属性的修改后,如果我们正在一个事务中,通常会在事务提交时或在显式调用 flush() 方法时将变化同步到数据库。在我们提供的测试方法中,如果没有显式调用 BaseEntityUtils.flush(),并且也没有使用 Spring 的事务管理(例如,使用 @Transactional 注解),那么在测试方法执行完毕时,可能并没有执行事务提交和 flush 操作,因此对 userInfo 和 userPassword 的属性修改可能尚未同步到数据库。在实际应用中,确保在需要同步变化时执行事务提交或显式调用 flush 方法。

持久态的前提是什么

需要在同一个EntityManager中才能被自动更新到数据库中。

持久态的前提是实体对象必须处于同一个持久化上下文(Persistence Context)中,而持久化上下文通常对应于一个 EntityManager。持久态实体对象是通过 EntityManager 进行管理和跟踪的。具体来说,以下是实体对象变为持久态的前提条件

通过 EntityManager 进行持久化操作

持久态的实体对象通常是通过 EntityManager 的 persist、merge、find 等方法从数据库中加载或保存得到的。在这些操作中,实体对象会被添加到 EntityManager 的持久化上下文中,从而成为持久态。

EntityManager entityManager = // 获取 EntityManager 的方式
UserInfo userInfo = new UserInfo();
entityManager.persist(userInfo); // 将实体对象变为持久态

通过查询操作获取的实体对象

当通过查询操作从数据库中获取实体对象时,这些对象也会成为持久态。例如,通过 find 方法获取对象

EntityManager entityManager = // 获取 EntityManager 的方式
UserInfo userInfo = entityManager.find(UserInfo.class, 1L); // 获取持久态对象

在这种情况下,userInfo 对象会被添加到 EntityManager 的持久化上下文中。

级联关系的影响

如果实体对象之间存在级联关系,并且级联操作的范围包括持久化操作,那么在级联操作中涉及到的对象也会变为持久态。

UserInfo userInfo = new UserInfo();
UserPassword userPassword = new UserPassword();
userInfo.setUserPassword(userPassword); // 设置级联关系

在这个例子中,如果级联关系包括 CascadeType.PERSIST,那么当 userInfo 被保存时,userPassword 也会成为持久态。总体而言,持久态的前提是实体对象必须由 EntityManager 进行管理,且这些实体对象需要处于同一个持久化上下文中。这确保了对实体对象的任何修改都能被 EntityManager 跟踪并在适当的时候同步到数据库。

问题分析

@Test
    public void demo3(){
        UserInfo userInfo = new UserInfo();
        userInfo.user_name = "demo123";
        userInfo.user_sex = 1;
        userInfo.user_age = 18;
        UserPassword userPassword = new UserPassword();
        userPassword.password = "demo-password";
        userInfo.userPassword = userPassword;
        userPassword.UserPassword_userInfo = userInfo;
        BaseEntityUtils.save(userInfo);
        System.out.println(userInfo.user_name);
        userInfo.user_name = "demo1234";
        userPassword.password = "demo1234-password";
        UserInfo new1 = BaseEntityUtils.findById(userInfo.id,UserInfo.class);
        System.out.println(new1.user_name);
    }

Alt textAlt text

在上面的代码中,由于使用的是静态方法去save,所以查询方法和Save方法并不是使用的一个entityManager,所以此时这两个对象的修改没有被同步到数据库中,两次打印都是demo123。

在我们提供的代码中,确实存在使用不同的 EntityManager 的情况,导致了持久态对象的状态在不同的 EntityManager 中不同步。具体来说,在 BaseEntityUtils.save(userInfo) 方法中使用了一个 EntityManager 对 userInfo 进行了保存,而在查询方法 BaseEntityUtils.findById(userInfo.id, UserInfo.class) 中使用了另一个 EntityManager 进行查询。由于每个 EntityManager 管理着自己的持久化上下文,所以在不同的 EntityManager 中,对相同的实体对象进行的修改不会同步到数据库。这就解释了为什么在查询方法中打印的 new1.user_name 仍然是 "demo123"。要解决这个问题,确保在同一个 EntityManager 中进行保存和查询操作,以保持持久态对象在相同的上下文中。

改进方案

@Test
    public void demo4(){
        UserInfo userInfo = new UserInfo();
        userInfo.user_name = "demo123";
        userInfo.user_sex = 1;
        userInfo.user_age = 18;
        UserPassword userPassword = new UserPassword();
        userPassword.password = "demo-password";
        userInfo.userPassword = userPassword;
        userPassword.UserPassword_userInfo = userInfo;
        UserInfoDomain userInfoDomain = new UserInfoDomain();
        userInfoDomain.setDomainEntity(userInfo);
        userInfoDomain.save();
        System.out.println(userInfo.user_name);
        userInfo.user_name = "demo1234";
        userPassword.password = "demo1234-password";
        userInfoDomain.flush();
        UserInfo new1 = userInfoDomain.findById(userInfo.id);
        System.out.println(new1.user_name);
    }

运行结果为:

Alt textAlt text

在UserInfoDomain中,所有的操作都在一个EntityManager里面,所以这两次打印分别是demo123和demo1234。

在我们提供的代码中,UserInfoDomain 类的设计确保了所有的操作都在同一个 EntityManager 中,这是非常好的实践。由于 UserInfoDomain 中的 save() 方法内部使用的是同一个 EntityManager 进行保存操作,保证了持久态对象在相同的上下文中。以下是对我们的代码的解释

保存操作
UserInfoDomain userInfoDomain = new UserInfoDomain();
userInfoDomain.setDomainEntity(userInfo);
userInfoDomain.save();

在这里,userInfoDomain.save() 方法内部使用了相同的 EntityManager 进行保存操作。因此,userInfo 对象及其关联的 userPassword 对象都处于持久态,保存到数据库。

第一次打印
System.out.println(userInfo.user_name);

这里打印的是 userInfo 对象的 user_name 属性,即 "demo123"。由于保存操作是在同一个 EntityManager 中执行的,所以在持久态下的 userInfo 对象的属性是最新的。

修改操作
userInfo.user_name = "demo1234";
userPassword.password = "demo1234-password";

这里对 userInfo 对象进行了属性的修改。

查询操作
UserInfo new1 = userInfoDomain.findById(userInfo.id);

在这里,findById 方法内部也是使用了相同的 EntityManager 进行查询操作。因此,获取的 new1 对象是 userInfo 对象在数据库中的最新状态。

第二次打印
System.out.println(new##### user_name);

这里打印的是经过查询操作后的 new1 对象的 user_name 属性,即 "demo1234"。由于查询操作也是在相同的 EntityManager 中执行的,所以能够获取到最新的数据库状态。总体来说,通过确保在同一个 EntityManager 中进行保存和查询操作,我们有效地维持了一致的持久化上下文,确保对象的状态能够正确同步到数据库。这是良好的实践,可以有效避免对象状态不同步的问题。

责任编辑:武晓燕 来源: 海燕技术栈
相关推荐

2009-09-24 15:42:44

Hibernate对象

2009-09-29 16:11:45

Hibernate实现

2009-09-23 17:00:07

Hibernate持久

2009-07-02 09:38:17

Hibernate延时

2009-09-25 14:41:15

Hibernate对象

2009-09-23 16:30:01

Hibernate f

2012-06-02 00:55:44

HibernateflushJava

2014-08-07 09:48:40

2009-09-29 10:37:29

Hibernate持久

2009-09-29 10:57:25

设置Hibernate

2009-09-21 17:46:34

Hibernate持久

2023-10-26 11:39:54

Linux系统CPU

2017-08-16 16:20:01

Linux内核态抢占用户态抢占

2009-09-24 16:39:20

Hibernate传播

2009-06-16 14:11:36

Hibernate优点Hibernate构架

2009-09-28 15:50:53

Hibernate创建持久化Product

2009-09-27 09:55:38

Hibernate持久

2009-09-29 16:46:01

创建Hibernate

2009-09-25 17:19:28

Hibernate持久

2009-06-29 08:50:20

Hibernate主键
点赞
收藏

51CTO技术栈公众号