在我们的应用程序中,我们希望根据当前状态更新一些记录。在数据库表中,我们有两个名为status的状态并通知,当状态为活动状态,并且通知为0时,我们需要更新通知为1.由于数据将被连续插入和更新,我写了一个线程来定期扫描表。 这是线程的伪代码:
public class DataPublisher implements Runnable {
private JPADAO dao;
public DataPublisher(JPADAO dao) {
this.dao = dao;
}
@Override
public void run() {
List<Entity> list = dao.getUpdateInformation();
for(Entity entity : list) {
dao.updateNotifiedStatus(entity.getId());
}
}
}
我使用预定的线程池运行该线程。 我用jpa来更新数据库。更新状态的代码如下:
public class JPADAO {
private EntityManager em;
public JPADAO(EntityManager em) {
if(em == null)
throw new NullPointerException("Invalid em argument.");
this.em = em;
}
public void updateNotifiedStatus(String id) {
EntityTransaction transaction = em.getTransaction();
transaction.begin();
Entity request = em.find(Entity.class, id);
request.setNotified(1);
transaction.commit();
}
当我启动应用程序时,问题是,第一次,所有数据都将正确更新。当我手动将使用数据库客户端通知的字段更改为0时,线程可以发现它们与要更新的条件匹配。但他们永远不会再更新为1。 为什么数据不会在线程中更新?
只是更新: 我已经解决了这个问题。它的主要原因是在我这边,我的进程将使用jpa访问数据库。但是其他进程也以不同的方式访问同一个进程。因为jpa默认会从缓存中读取数据。每次在我的查询中,它只会从缓存中获取数据而不知道数据库已更改。因此,要从数据库获取最新的更新数据,我们需要在eclipselink中禁用缓存。这是我添加的配置:
<property name="eclipselink.query-results-cache" value="false"/>
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.cache.size.default" value="0"/>
<property name="eclipselink.cache.type.default" value="None"/>
答案 0 :(得分:0)
首先,这看起来应该在实体中处理,因此如果状态和通知值分别更改为active和0,则业务逻辑本身会将通知值更改为1.
其次,我不确定如何在池中使用DataPublisher实例,但只有一个实例可以重用,这意味着你反复使用相同的EntityManager。因为EntityManagers缓存托管实例,所以每个find调用都返回它用该id缓存的实体 - 第一个调用从db加载数据,而后续迭代只获取缓存数据。缓存的实体已将其通知值设置为1,因此JPA无需更改。
有几种方法可以解决这个问题。完成后关闭dao中的em并在需要时获取新的em;事务提交后清除em以便可以重用它并释放实体当前状态,或者在实体上使用em.refresh强制更改从数据库中拉入上下文。