我正在使用Java,并学习如何使用持久性API (toplink-essentials)来管理Derby DB中的实体。注:这是(远程学习)大学的工作,但这不是“家庭作业”这个问题出现在课程材料中。
我有两个线程在同一组实体上运行。我的问题是,我尝试过的每一种方法,都可以修改一个线程中查询结果集中(在事务中执行的查询)中的实体,以便结果集对事务的其余部分不再有效。
例如,从一个线程执行此操作:
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");
}如果使用此方法从另一个线程中断:
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基本要素没有悲观的锁定)。
发布于 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的悲观锁定:
// 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查询。
发布于 2010-07-14 23:25:50
您可能需要查看EntityManager.lock()方法,该方法允许您在事务初始化后获得实体的乐观锁或悲观锁。
按照您对问题的描述,一旦从数据库中“选择”了数据库记录,您希望锁定它。这可以通过悲观锁来实现,这种锁或多或少相当于选择.来自tbl的UPDATE语句。
https://stackoverflow.com/questions/3251191
复制相似问题