今天继续学习JPA中实体的最后一种状态:移除态。还是先把图晒出来:
按照名称进行猜想,移除态应该就是对象从数据库中删除以后,对象变成了不受持久化上下文管理的移除态。不过呢,这里有一点需要注意的是:
什么样的对象才会进入进入移除态呢?
变成移除状态的对象是调用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()方法进入持久化状态的。至于为什么这样设计,我也暂时还没有想明白,如果有知道的欢迎给我留言,不甚感谢。