首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Java中使用持久性API防止不可重复的查询结果?

如何在Java中使用持久性API防止不可重复的查询结果?
EN

Stack Overflow用户
提问于 2010-07-14 22:55:58
回答 2查看 2.6K关注 0票数 3

我正在使用Java,并学习如何使用持久性API (toplink-essentials)来管理Derby DB中的实体。注:这是(远程学习)大学的工作,但这不是“家庭作业”这个问题出现在课程材料中。

我有两个线程在同一组实体上运行。我的问题是,我尝试过的每一种方法,都可以修改一个线程中查询结果集中(在事务中执行的查询)中的实体,以便结果集对事务的其余部分不再有效。

例如,从一个线程执行此操作:

代码语言:javascript
复制
static void updatePrices(EntityManager manager, double percentage) {
    EntityTransaction transaction = manager.getTransaction();

    transaction.begin();
    Query query = manager.createQuery("SELECT i FROM Instrument i where i.sold = 'no'");
    List<Instrument> results = (List<Instrument>) query.getResultList();

    // force thread interruption here (testing non-repeatable read)
    try { Thread.sleep(2000); } catch (Exception e) { }

    for (Instrument i : results) {
        i.updatePrice(percentage);
    }
    transaction.commit();
    System.out.println("Price update commited");
}

如果使用此方法从另一个线程中断:

代码语言:javascript
复制
private static void sellInstrument(EntityManager manager, int id)
{
    EntityTransaction transaction = manager.getTransaction();
    transaction.begin();
    Instrument instrument = manager.find(Instrument.class, id);
    System.out.println("Selling: " + instrument.toFullString());
    instrument.setSold(true);
    transaction.commit();
    System.out.println("Instrument sale commited");
}

可能发生的情况是,当updatePrices()中的线程恢复其查询resultSet时,它的查询resultSet是无效的,并且已售出项目的价格最终会被更新到与出售时不同的价格。(商店希望保存在DB中出售的物品的记录)。由于存在并发事务,所以我对每个线程使用不同的EntityManager (来自同一个工厂)。

是否有可能(通过锁定或某种上下文传播)防止查询结果在(中断)事务期间变得“无效”?我有一个想法,这种场景就是Java的用途,但我想知道的是它在Java中是否可行。

编辑:

接受Vineet和Pascal的建议:在实体的类中使用@Version注释(带有额外的DB列)会导致大型事务( updatePrices() )在OptimisticLockException中失败。但是,如果在大量查询结果结束时发生这种情况,这是非常昂贵的。是否有任何方法使我的查询(在updatePrices()中)锁定相关行,从而导致sellInstrument()中的线程阻塞或中止抛出异常(然后中止)?这样会便宜得多。(据我所知,我对Toplink基本要素没有悲观的锁定)。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-07-14 23:51:38

螺纹安全

我对你管理你的EntityManager的方式有疑问。虽然EntityManagerFactory是线程安全的(并且应该在应用程序启动时创建一次),但是EntityManager不是,您通常应该每个线程使用一个EntityManager (或者同步对它的访问,但我会使用每个线程一个EntityManager)。

并发性

JPA1.0支持(仅)乐观锁定(如果使用Version属性)和两种锁模式,允许通过EntityManager.lock() API避免脏读和不可重复读取。我建议阅读JPA1.0规范的读写锁定和/或整个JPA1.0规范的3.4乐观锁定和并发以获得详细信息。

PS:注意,在JPA1.0中不支持悲观锁定,或者仅通过特定于提供程序的扩展(它已经添加到JPA2.0中,以及其他锁定选项)。以防万一,Toplink通过eclipselink.pessimistic-lock查询提示支持它。

正如在JPA中所写的,在JPA1.0中,应该通过一个查询提示来支持TopLink的悲观锁定:

代码语言:javascript
复制
// eclipselink.pessimistic-lock
Query Query = em.createQuery("select f from Foo f where f.bar=:bar");
query.setParameter("bar", "foobar");
query.setHint("eclipselink.pessimistic-lock", "Lock");
query.getResultList();

我不使用TopLink,所以我无法确认所有版本都支持这个提示。如果不是,则如果要生成“用于更新”,则必须使用本机SQL查询。

票数 3
EN

Stack Overflow用户

发布于 2010-07-14 23:25:50

您可能需要查看EntityManager.lock()方法,该方法允许您在事务初始化后获得实体的乐观锁或悲观锁。

按照您对问题的描述,一旦从数据库中“选择”了数据库记录,您希望锁定它。这可以通过悲观锁来实现,这种锁或多或少相当于选择.来自tbl的UPDATE语句。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3251191

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档