对象的状态

通过 JPA 对实体对象进行增删改查时,JPA 需要维护存储在 Session(即一级缓存)中的实体对象,同时还要维护实体对象的状态。

实体对象的状态是 JPA 中非常重要的概念,它描述了实体对象从瞬时状态(Transient)到持久状态(Persistent)、从删除状态(Removed)到游离状态(Detached)的状态变换。JPA 中对实体的操作主要就是改变实体的状态。

EntityManager 提供一系列的方法管理实体对象的状态,如下:

  • persist:将新创建的或已删除的实体转变为持久化状态,数据存入数据库

  • remove:删除持久状态的实体,实体转变为删除状态

  • merge:将游离实体转变为持久化状态,数据存入数据库

如果使用了事务管理,则事务的 commit/rollback 也会改变实体的状态,实体状态变化如下图:

瞬时状态(Transient)

瞬时状态的实体就是一个普通的 java 对象,和持久化上下文无关联,数据库中也没有数据与之对应。

我们使用 new 关键字创建出来的新对象就是瞬时状态,该对象没有OID,且不在一级缓存中。

持久状态(Persistent)

JPA 中调用持久化方法 persist() 之后,将对象保存到数据库中,对象状态转化成持久状态。

你可以使用 EntityManager 进行 find() 或者 persist() 操作返回的对象将处于托管状态,此时该对象已经处于持久化上下文中,因此任何对于该实体的更新都会同步到数据库中。

游离状态(Detached)

JPA 中对象存在于数据库中,但是不在一级缓存中,此时对象位于游离状态。

当事务提交后,处于托管状态的对象就转变为了游离状态。此时该对象已经不处于持久化上下文中,因此任何对于该对象的修改都不会同步到数据库中。

删除状态(Removed)

JPA 中事务一旦提交,对象就会被从数据库中删除(delete 操作),此时实体对象介于持久状态和被删除之间的一个临界状态。

我们可以通过下面的表格了解到各个状态的特点:

状态是否在一级缓存是否有OID
瞬时状态(Transient)
持久状态(Persistent)
游离状态(Detached)
删除状态(Removed)

注意:其中的 OID 即对象 ID(Object ID)

示例

上面有了对实体对象状态的了解之后,我们来分析下面的案例中 SQL 的发送。下面实例查询ID为1的用户,然后修改 name,但是不手动执行 merge 操作。Java 代码如下:

public void demo() throws Exception {
    EntityManagerFactory managerFactory = null;
    EntityManager entityManager = null;
    try {
        managerFactory = Persistence.createEntityManagerFactory(PERSISTENCE_NAME, System.getProperties());
        entityManager = managerFactory.createEntityManager();
        entityManager.getTransaction().begin();

        // 获取一个 User 对象,然后修改 name,但是不手动执行 merge 操作
        User user = entityManager.find(User.class, 1); // ①
        user.setName("update-" + user.getName());
        //entityManager.merge(user);

        entityManager.getTransaction().commit();
    } finally {
        if (null != entityManager) {
            entityManager.close();
        }
        if (null != managerFactory) {
            managerFactory.close();
        }
    }
}

执行结果:

2322  openJPA  TRACE  [main] openjpa.jdbc.SQL - <t 2012744708, conn 31114735> executing prepstmnt 1312381159 SELECT t0.age, t0.birthday, t0.name, t0.salary FROM User t0 WHERE t0.id = ? [params=?]
2372  openJPA  TRACE  [main] openjpa.jdbc.SQL - <t 2012744708, conn 31114735> executing prepstmnt 1706292388 UPDATE User SET name = ? WHERE id = ? [params=?, ?]

分析

上面实例中,在 ① 位置处,我们修改了查询出来处于持久状态的 User 对象的 name 属性的值。我们并没有调用 merge 方法去更新 User 对象,为什么会发送 update 语句呢?

原因

首先,将数据从数据库中查询出来后,在内存中会有两份数据:

(1)在 EntityManager 一级缓存区域

(2)在 EntityManager 的快照区

上面两份数据完全一样。

然后,修改 User 的 name 属性时,其实是修改的缓存区的数据

最后,在提交事务的时候,会清理一级缓存;此时会对比两份数据是否一致?如果不一致,发送对应的 update 语句将缓存中的脏数据(和数据库中的数据不一致)同步到数据库中。

所以,在上面的例子中,我们看到执行了一条更新语句,这样相信大家就能够理解了,这也是在我们了解了对象的状态之后对 SQL 的发送有了更深入的认识。

说说我的看法
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号