Java 多线程应用程序的JPA原子查询/保存
我正在修改我的JPA代码以利用线程。我有一个单独的实体管理器和每个线程的事务 我以前(对于单线程环境)的代码如下:Java 多线程应用程序的JPA原子查询/保存,java,jpa,Java,Jpa,我正在修改我的JPA代码以利用线程。我有一个单独的实体管理器和每个线程的事务 我以前(对于单线程环境)的代码如下: // get object from the entity manager X x = getObjectX(jpaQuery); if(x == null) { x = new X(); x.setVariable(foo); entityManager.persist(x); } 对于多线程环境中的代码,我得到了重复的键,因为我假设getObject
// get object from the entity manager
X x = getObjectX(jpaQuery);
if(x == null)
{
x = new X();
x.setVariable(foo);
entityManager.persist(x);
}
对于多线程环境中的代码,我得到了重复的键,因为我假设getObjectX为一个线程返回null,然后该线程被交换出去,下一个线程调用getObjectX,也得到null,然后两个线程将创建并持久化一个新的X()
除了添加同步之外,如果JPA中不存在值,是否有一种原子方式来获取/保存,或者我是否应该重新考虑我的方法
编辑:
我正在使用最新的Eclipselink和MySql 5.1
编辑2:
我添加了同步。。。巨大的性能损失(以至于无法使用)。将在主线程上收集所有数据,然后在该线程上进行创建。我认为您需要在“jpaQuery”中使用的字段上添加一个唯一的约束,以便数据库不能使用该查询的约束中使用的相同条件创建重复行。调用代码将需要捕获由于约束冲突而产生的异常(理想情况下是EntityExistsException,但在这种情况下规范不明确)。是否确实需要多个EntityManager?在类似的情况下,我只使用一个entitymanager和简单的每个方法锁定对象:
private Object customerLock = new Object[0];
public Customer createCustomer(){
Customer customer = new Customer();
synchronized(customerLock){
entityManager.persist(customer);
}
return customer;
}
编辑:好的,除了说它在我的应用程序中运行正常外,对性能没什么影响,但为了唯一性,请使用以下内容:
public Customer getOrCreateCustomer(String firstName, String lastName){
synchronized(customerLock){
List<Customer> customers =
entityManager.createQuery(
"select c from Customer c where c.firstName = :firstName"
+ " and c.lastName = :lastName"
)
.setParam("firstName", firstName)
.setParam("lastName", lastName)
.setMaxResults(1)
.getResultList();
if(customers.isEmpty()){
Customer customer = new Customer(firstName, lastName);
entityManager.persist(customer);
}else{
customer = customers.get(0);
}
}
return customer;
}
public Customer getOrCreateCustomer(String firstName,String lastName){
已同步(customerLock){
列出客户=
entityManager.createQuery(
“从客户c中选择c,其中c.firstName=:firstName”
+“和c.lastName=:lastName”
)
.setParam(“名字”,名字)
.setParam(“lastName”,lastName)
.setMaxResults(1)
.getResultList();
if(customers.isEmpty()){
客户=新客户(名字、姓氏);
entityManager.persist(客户);
}否则{
customer=customers.get(0);
}
}
退货客户;
}
简而言之,悲哀的答案是否定的,JPAAPI不能为您做到这一点。整个API或多或少都是围绕着乐观的原则构建的,即假设一切正常,并在并发修改的情况下抛出异常
如果这种情况经常发生,那么可能还有其他组件(生成foo的是什么?)可以从线程安全中获益,作为围绕查询+创建进行同步的替代方案。需要考虑一些“黑客”问题:
- 基于对象的业务密钥(而不是生成的id)实现
hashCode()
和equals()
- 同步时间:
(obj.getClass().getName() + String.valueOf(hashCode())).intern()
因此,您将仅在相关情况下获得锁。我确实有唯一的约束,我得到了例外。我希望找到一种不引发异常的方法…在这种情况下,使用异常可能是最简单的解决方案,类似于乐观锁定,在失败时也使用异常。我需要确保对象(在您的情况下)不存在客户。我尝试添加同步,但性能非常差。我必须反过来做,创建Foo的东西是我无法控制的,而且几乎可以保证会有重复的。我必须收集线程中的所有数据,然后将它们存储在单个线程中。嗯。。。我已经有了这样的等号/哈希码。。。从未想过在它们上同步。我要试试看。