在线程中使用jpa更新数据库的问题

时间:2014-03-15 10:46:00

标签: java multithreading jpa eclipselink

在我们的应用程序中,我们希望根据当前状态更新一些记录。在数据库表中,我们有两个名为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"/>

1 个答案:

答案 0 :(得分:0)

首先,这看起来应该在实体中处理,因此如果状态和通知值分别更改为active和0,则业务逻辑本身会将通知值更改为1.

其次,我不确定如何在池中使用DataPublisher实例,但只有一个实例可以重用,这意味着你反复使用相同的EntityManager。因为EntityManagers缓存托管实例,所以每个find调用都返回它用该id缓存的实体 - 第一个调用从db加载数据,而后续迭代只获取缓存数据。缓存的实体已将其通知值设置为1,因此JPA无需更改。

有几种方法可以解决这个问题。完成后关闭dao中的em并在需要时获取新的em;事务提交后清除em以便可以重用它并释放实体当前状态,或者在实体上使用em.refresh强制更改从数据库中拉入上下文。