Hen*_*ryR 23 java database hibernate jpa
我的JPA/Hibernate应用程序中有几个映射对象.在网络上,我接收代表这些对象更新的数据包,或者实际上可能完全代表新对象.
我想写一个像这样的方法
<T> T getOrCreate(Class<T> klass, Object primaryKey)
Run Code Online (Sandbox Code Playgroud)
如果一个人在PK的PrimaryKey数据库存在,否则创建该类的一个新对象,持续,并将其返回,返回所提供的类的对象.
我将对该对象做的下一件事是在事务中更新其所有字段.
在JPA中有没有惯用的方法,或者有更好的方法来解决我的问题?
Pas*_*ent 20
我想写一个像这样的方法
<T> T getOrCreate(Class<T> klass, Object primaryKey)
这并不容易.
一种天真的方法是做这样的事情(假设方法在事务中运行):
public <T> T findOrCreate(Class<T> entityClass, Object primaryKey) {
T entity = em.find(entityClass, primaryKey);
if ( entity != null ) {
return entity;
} else {
try {
entity = entityClass.newInstance();
/* use more reflection to set the pk (probably need a base entity) */
return entity;
} catch ( Exception e ) {
throw new RuntimeException(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是在并发环境中,由于某些竞争条件,此代码可能会失败:
T1: BEGIN TX; T2: BEGIN TX; T1: SELECT w/ id = 123; //returns null T2: SELECT w/ id = 123; //returns null T1: INSERT w/ id = 123; T1: COMMIT; //row inserted T2: INSERT w/ name = 123; T2: COMMIT; //constraint violation
如果您运行多个JVM,同步将无济于事.并且没有获得表锁(这非常可怕),我真的不知道如何解决这个问题.
在这种情况下,我想知道系统地插入第一个并处理可能的异常以执行后续选择(在新事务中)是否更好.
您应该添加一些有关上述约束的细节(多线程?分布式环境?).
使用纯JPA可以在具有嵌套实体管理器的多线程解决方案中很好地解决此问题(确实,我们只需要嵌套事务,但我认为纯JPA不可能做到这一点)。本质上,需要创建一种微交易,其中包含查找或创建操作。这种性能不会太出色,也不适合用于大批量创建,但是对于大多数情况来说应该足够了。
先决条件:
finderfactory。码:
public <T> T findOrCreate(Supplier<T> finder, Supplier<T> factory) {
EntityManager innerEntityManager = entityManagerFactory.createEntityManager();
innerEntityManager.getTransaction().begin();
try {
//Try the naive find-or-create in our inner entity manager
if(finder.get() == null) {
T newInstance = factory.get();
innerEntityManager.persist(newInstance);
}
innerEntityManager.getTransaction().commit();
} catch (PersistenceException ex) {
//This may be a unique constraint violation or it could be some
//other issue. We will attempt to determine which it is by trying
//to find the entity. Either way, our attempt failed and we
//roll back the tx.
innerEntityManager.getTransaction().rollback();
T entity = finder.get();
if(entity == null) {
//Must have been some other issue
throw ex;
} else {
//Either it was a unique constraint violation or we don't
//care because someone else has succeeded
return entity;
}
} catch (Throwable t) {
innerEntityManager.getTransaction().rollback();
throw t;
} finally {
innerEntityManager.close();
}
//If we didn't hit an exception then we successfully created it
//in the inner transaction. We now need to find the entity in
//our outer transaction.
return finder.get();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
36481 次 |
| 最近记录: |