JPA实体状态学习-(移除态:Removed)

时间:2022-06-01 21:21:37

今天继续学习JPA中实体的最后一种状态:移除态。还是先把图晒出来:

JPA实体状态学习-(移除态:Removed)

按照名称进行猜想,移除态应该就是对象从数据库中删除以后,对象变成了不受持久化上下文管理的移除态。不过呢,这里有一点需要注意的是:

什么样的对象才会进入进入移除态呢?

变成移除状态的对象是调用persist()方法后,是像persist一样参数对象受到持久化上下文管理呢?还是像merge方法一样返回的对象受到持久化上下文管理呢?

这里还有一个显而易见的问题,移除的对象再次进入持久化以后,猜测提交事务以后肯定是发出一条insert的SQL语句。有没有可能会发出update语句呢?

针对上面的三个问题,我们会一个一个通过代码来解决:

第一种情况:直接删除Transient状态的对象

public void removedToPersist() {
    Teacher teacher = new Teacher("new_email@gmail.com");
    logger.info("teacher: {} is transient state",teacher);
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction tx = entityManager.getTransaction();
    tx.begin();
    entityManager.remove(teacher);
    logger.info("teacher: {} is removed state",teacher);
    Long persistedId = teacher.getId();
    tx.commit();
    entityManager.close();
}

日志信息如下:

teacher: Student{id=null, email='new_email@gmail.com'} is transient state
Handling transient entity in delete processing
teacher: Student{id=null, email='new_email@gmail.com'} is persistent state

可以看出这种情况下,没有发出任何SQL,也不应该发出任何SQL

第二种情况:直接删除Persistent状态的对象:

public void removedToPersist() {
    Teacher teacher = new Teacher("new_email@gmail.com");
    logger.info("teacher: {} is transient state",teacher);
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction tx = entityManager.getTransaction();
    tx.begin();
    entityManager.persist(teacher);
    logger.info("teacher: {} is persist state",teacher);
    Long persistedId = teacher.getId();
    tx.commit();
    entityManager.close();

    entityManager = entityManagerFactory.createEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    teacher = entityManager.find(Teacher.class, persistedId);
    entityManager.remove(teacher);
    tx.commit();
    entityManager.close();
    logger.info("teacher: {} is removed state",teacher);
}
teacher: Student{id=null, email='new_email@gmail.com'} is transient state
Hibernate: 
   call next value for hibernate_sequence
teacher: Student{id=1, email='new_email@gmail.com'} is persist state
Hibernate: 
   insert 
   into
       teachers
       (email, id) 
   values
       (?, ?)
Hibernate: 
   select
       teacher0_.id as id1_0_0_,
       teacher0_.email as email2_0_0_ 
   from
       teachers teacher0_ 
   where
       teacher0_.id=?
Hibernate: 
   delete 
   from
       teachers 
   where
       id=?
teacher: Student{id=1, email='new_email@gmail.com'} is removed state

从上面的代码和日志信息可以看出,当处于持久态的对象执行删除的时候,对象可以正常删除。

如果对象处于游离态执行删除会怎么样呢?我们来试试:

我们将上面的

teacher = entityManager.find(Teacher.class, persistedId);

修改为:

logger.info("delete detached state teacher:{}",teacher);

执行之后,打印的日志如下:

teacher: Student{id=null, email='new_email@gmail.com'} is transient state
Hibernate: 
   call next value for hibernate_sequence
teacher: Student{id=1, email='new_email@gmail.com'} is persist state
Hibernate: 
   insert 
   into
       teachers
       (email, id) 
   values
       (?, ?)
delete detached state teacher:Student{id=1, email='new_email@gmail.com'}
java.lang.IllegalArgumentException: Removing a detached instance com.jpa.demo.model.Teacher#1

因此,这里需要注意的是:删除游离态的对象会抛出一个异常:Removing a detached instance

NOTE: Only persistent objects can be removed in JPA, any attempt to delete a dettached object will cause java.lang.IllegalArgumentException: Removing a detached instance.

entityManager的移除方法签名如下:

public void remove(Object entity);

因此我们猜测,当移除态的对象按照最上面的状态迁移图再次调用persist()方法后,应该是参数对象被持久化上下文管理,我们不妨用代码试试:

这次的代码如下:

public void removedToPersist() {
        Teacher teacher = new Teacher("new_email@gmail.com");
        logger.info("teacher: {} is transient state",teacher);
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();
        entityManager.persist(teacher);
        logger.info("teacher: {} is persist state",teacher);
        Long persistedId = teacher.getId();
        tx.commit();
        entityManager.close();

        entityManager = entityManagerFactory.createEntityManager();
        tx = entityManager.getTransaction();
        tx.begin();
        teacher = entityManager.find(Teacher.class, persistedId);
        entityManager.remove(teacher);
        entityManager.persist(teacher);
        tx.commit();
        entityManager.close();
        logger.info("teacher: {} is removed state",teacher);

//        entityManager = entityManagerFactory.createEntityManager();
//        tx = entityManager.getTransaction();
//        tx.begin();
//        entityManager.persist(teacher);
//        logger.info("teacher: {} is persist state",teacher);
//        tx.commit();
//        entityManager.close();
    }

我们的猜想应该最先是一条insert语句,然后是一条查询语句,然后是一条delete语句,最后是一条insert语句。哈哈,看看日志信息吧:

teacher: Student{id=null, email='new_email@gmail.com'} is transient state
Hibernate: 
   call next value for hibernate_sequence
teacher: Student{id=1, email='new_email@gmail.com'} is persist state
Hibernate: 
   insert 
   into
       teachers
       (email, id) 
   values
       (?, ?)
Hibernate: 
   select
       teacher0_.id as id1_0_0_,
       teacher0_.email as email2_0_0_ 
   from
       teachers teacher0_ 
   where
       teacher0_.id=?
teacher: Student{id=1, email='new_email@gmail.com'} is removed state

哈哈,有意思,既没有删除,也么有插入,大家理解了吧,有些东西还真不是想当然的。

这里再补充一个知识点:

detached状态的对象,调用persist()方法会怎么样呢?

public void removedToPersist() {
    Teacher teacher = new Teacher("new_email@gmail.com");
    logger.info("teacher: {} is transient state",teacher);
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction tx = entityManager.getTransaction();
    tx.begin();
    entityManager.persist(teacher);
    logger.info("teacher: {} is persist state",teacher);
    Long persistedId = teacher.getId();
    tx.commit();
    entityManager.close();

    entityManager = entityManagerFactory.createEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    teacher = entityManager.find(Teacher.class, persistedId);
    entityManager.remove(teacher);

    tx.commit();
    entityManager.close();
    logger.info("teacher: {} is removed state",teacher);

    entityManager = entityManagerFactory.createEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    entityManager.persist(teacher);
    logger.info("teacher: {} is persist state",teacher);
    tx.commit();
    entityManager.close();
}

我们知道在最上面的状态迁移图中,detached可以通过merge方法进入persist状态,结果detached状态通过调用persist()方法后,让人吃惊:

teacher: Student{id=null, email='new_email@gmail.com'} is transient state
Hibernate: 
   call next value for hibernate_sequence
teacher: Student{id=1, email='new_email@gmail.com'} is persist state
Hibernate: 
   insert 
   into
       teachers
       (email, id) 
   values
       (?, ?)
Hibernate: 
   select
       teacher0_.id as id1_0_0_,
       teacher0_.email as email2_0_0_ 
   from
       teachers teacher0_ 
   where
       teacher0_.id=?
Hibernate: 
   delete 
   from
       teachers 
   where
       id=?
[INFO] 2021-12-13 23:11:29 method: com.jpa.demo.dao.EntityStatusDemoDao.removedToPersist(EntityStatusDemoDao.java:133)----teacher: Student{id=1, email='new_email@gmail.com'} is removed state
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.jpa.demo.model.Teacher

结果抛出了一个异常:PersistenceException

也就是说,detached态时不能通过persist()方法进入持久化状态的。至于为什么这样设计,我也暂时还没有想明白,如果有知道的欢迎给我留言,不甚感谢。