我遇到的问题类似于Invalidating JPA EntityManager session中描述的问题:
问题:获取陈旧数据
我们在SQL数据库上运行JPQL查询,该数据库也由不同的应用程序同时更改。我们正在使用在Tomcat下运行的JSF + Spring + EclipseLink。
执行JPQL查询的类是单例Spring bean,并使用注入的EntityManager
:
@Component
public class DataRepository{
@PersistenceContext
private EntityManager entityManager;
public List<MyDTO> getStuff(long id) {
String jpqlQuery ="SELECT new MyDTO([...])";
TypedQuery<MyDTO> query = entityManager.createQuery(jpqlQuery,MyDTO.class);
return query.getResultList();
}
[...]
(代码解释)。
问题是此代码没有看到直接对数据库执行的更改。只有重新启动Tomcat实例时,这些更改才会显示。
我们尝试了什么
我们假设此行为是由与EntityManager
关联的第一级缓存引起的,如链接问题中所述。我们找到了两个解决方案:
entityManager.clear()
之前调用createQuery
(这在链接问题中有建议)EntityManagerFactor
(使用@PersistenceUnit
),然后为每个查询创建并关闭新的EntityManager
两种解决方案都能满足我们的需求 - 我们获得了新数据。
问题:
entityManager.clear()
,还是会以某种方式影响其他使用注入的EntityManager的代码(在相同或不同的类中)?EntityManager
?我认为这必定是一个相当普遍的问题(因为只要多个应用程序共享一个数据库就会出现这种情况),所以我认为必须有一个简单的解决方案......
答案 0 :(得分:4)
看起来我们已经解决了这个问题。
实际上有两个问题:
在调试问题时,在某些情况下,我们没有为每个查询使用新的EntityManager。当使用和重新使用相同的EntityManager时,实体在检索后显然会从不刷新(除非使用EntityManager.refresh()
明确刷新)。这显然是因为一旦加载了实体,它就存储在EntityManager的持久化上下文(a.k.a.一级缓存)中。这样做是必要的,因为JPA规范要求对同一实体的后续查询返回相同的对象实例。换句话说:只要您使用相同的EntityManager,除非您明确刷新,否则您将获得陈旧数据。
当我们 为每个查询使用新的EntityManager时,通常会有效。但是,我们遇到a bug in EclipseLink,其中为某些JPQL查询返回过时数据(涉及构造函数表达式和JOIN FETCH)。
简短版
如果您想要始终从程序外部修改的数据库中获取新数据,那么
<shared-cache-mode>NONE</shared-cache-mode>
)答案 1 :(得分:2)
创建新的EntityManager
是正确的,另一个不是(见下文)。
除了单线程系统外,调用EntityManager#clear()
非常危险。
...导致所有管理实体分离......
因此,如果一个线程与附加实体一起工作,而“你的”线程清除实体管理器则会产生严重的副作用。
嗯,很难说。如果在您的应用程序之外修改的实体数量很少,我将直接使用数据源并使用JdbcTemplate
进行相应的操作。
答案 2 :(得分:1)
要在EclipseLink中禁用共享缓存,请参阅
http://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F
答案 3 :(得分:0)
这将是神奇的
entityManager.getEntityManagerFactory().getCache().evict(MyDTO.class);