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

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

Entity instance states

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

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

变成移除状态的对象是调用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()方法进入持久化状态的。至于为什么这样设计,我也暂时还没有想明白,如果有知道的欢迎给我留言,不甚感谢。

版权声明:著作权归作者所有。

相关推荐

JPA实体状态学习-(游离态:Detached)

今天我们学习另一种实体状态,老规矩还是先上图:Detached(游离态 unmanaged)An Object becomes detached when the currently running Persistence Context is closed. Any changes made to detached objects are no longer automatically prop

JPA实体状态学习-(瞬时态:Transient)

为了学习实体的状态,我们还是贴出这张实体状态转换迁移图:Transient(瞬时态)按照上图的描述,java对象在内存中被赋值后,没有调用entityManager.persist()方法之前实体对象所处的状态。举个例子:Teacher teacher = new Teacher("email@dot.com");此时,实例teacher就处于new/transient态(备注:这里的new和tr

JPA实体状态学习-(持久态:Persistent)

今天我们继续学习JPA的实体状态转化,老规矩贴上实体转化的转化图: 上一节我们学习了:entityManager调用persist()方法后,实体的状态从transient到persistent,这个时候对persistent状态的实体改变也会同步持久化到数据库,执行tx.commit()之后实体进入detached状态,此时对实体进行改变后不会同步到数据库。今天我们来看看transie

JPA实体状态深入理解

我们在学习JPA实体状态的时候,常常会问,JPA的实体有多少状态呢?相信这个问题不难回答:瞬时态(transient)托管态(persistent)游离态(detached)移除态(removed)注意:这里最后一个移除态,有的时候也叫删除态(deleted),至于它和移除态有啥区别,暂时没有想到,如果您对此有更加深刻的理解,请留言回复。为什么会有这四种状态呢?啥,这个也有为啥,网上不是都这么说的

获取curl的http response状态码

可以使用以下脚本获取curl请求返回的状态码:curl -w '%{response_code}\n' -so /dev/null $URL其中$URL是一个变量,可以使用实际测试的url替换。如请求百度:curl -w '%{response_code}\n' -so /dev/null https://baidu.com输出结果:302

thenjs 学习

thenjs 是一个js的异步控制流程库 特点 可以像标准的Promise那样,把N多异步回调函数写成一个长长的then链,并且比Promise更简洁自然。因为使用标准Promise的then链,其中的异步函数都必须转换成Promise,Thenjs则无需转换,像使用CallBack一样执行异步函数即可。 强大的Error机制,可以捕捉任何同步和异步的异步错误。 开启debug模式