这周工作过程中,组内遇到了一个缺陷:某实体对象在未显式调用save的前提下,进行了数据库更新。前段时间,我同样遇到此类问题,当时简单了解到,该实体对象处于缓存中,且与数据库存在关联。因此简单调用EntityManager中的detach解除了关联。
借此机会,希望较深入地探讨以下几方面内容:
1. 是谁在我们不知情的情况下,做了这一切?
2. 我之前理解的缓存,究竟是什么?
3. EneityManger的用法探讨。
4. 如何避免此类现象的发生?
是谁在我们不知情的情况下,做了这一切?
这个坑很隐蔽,但是踩坑的人很多。借助他人的剖析,我们不难发现,罪魁祸首是Spring-data-jpa。
什么是Spring-data-jpa?
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
Spring-data-jpa为啥要对我们的对象进行这类操作?
That’s a fundamental principle of JPA. You work with attached (managed) entities, and every modification made on these managed entities is automatically made persistent.
这句话有两个信息。
1:attached entities。继续查阅会发现,除了托管状态(persistent),还有瞬时状态(transient)、游离状态(detached)、删除状态 (deleted)。因此建立猜测,EntityManager是调控各个entity进行状态切换的组件。那么状态机各状态含义为何?状态机何时会进行状态流转?
2:every modification persistent automatically。因为之前了解到,Spring-data-jpa是存在有类似于缓存的机制,会存放这些持久化对象。那么,这个类似于缓存的机制原理为何?自动持久化也会有一个时机存在,它的触发机制是什么?
以上抛出的两组疑问,依次展开来讨论。
我之前理解的缓存,究竟是什么?
阅读stackoverflow相关问题的回复,会发现有一个术语出现的比较频繁——Persistence Context。联想到context一般作为环境存在,所以是不是持久化对象都会寄存在这个Persistence Context中呢?
So,什么是Persistence Context?
A persistence context is a set of entities such that for any persistent identity there is a unique entity instance. Within a persistence context, entities are managed. The EntityManager controls their lifecycle, and they can access datastore resources.
这也印证了猜测,确实是Persistence Context管理了Entities。
那么一个持久化上下文,是寄托于谁存在的,什么时候开启,又是什么时候结束呢?
Injected EntityManagers have a transaction persistence context, while EntityManagers obtained through the EntityManagerFactory have an extended persistence context. We describe these persistence context types below.
之前在使用EntityManager时,是采用注入的方式。那么看起来,该EntityManager就会持有一个Transaction Persistence Context。
Under the transaction persistence context model, an EntityManager begins a new persistence context with each transaction, and ends the context when the transaction commits or rolls back.
EntityManager控制下的各状态含义为何?什么时候会触发流转?
new, or transient - the entity has just been instantiated and is not associated with a persistence context. It has no persistent representation in the database and no identifier value has been assigned.
managed, or persistent - the entity has an associated identifier and is associated with a persistence context.
detached - the entity has an associated identifier, but is no longer associated with a persistence context (usually because the persistence context was closed or the instance was evicted from the context)
removed - the entity has an associated identifier and is associated with a persistence context, however it is scheduled for removal from the database.
这里搬一张状态流转机的图片:
可以看到,调用EntityManager的相关方法,即可控制实体对象在不同状态间转换。
如何避免此类现象的发生?
目前看来,最好的方案是进行对象分层。例如在业务层使用BO对象,此对象由Dao曾获取PO时转换而来。
借此避免PO对象在业务流程中进行属性的set操作。
参考信息
EntityManager 管理 Entity 的API
https://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html