[英]JPA merge() best practice
我為entity object
實現了一個服務,它使用了純jpa
,我使用了spring
,所以在spring xml config
配置hibernate
作為jpa
impl。 我正在使用spring
數據進行crud
操作。 但是在我們的系統中,我們的entity
對象被多次拉取/更新 ,並且數據存在高度爭用( concurrency
)。 從許多地方的代碼中,許多classes
只inject
服務bean並調用getEntity
方法來獲取實體,之后它們更改實體(根據我的理解分離,但在同一個線程中,因此em對象應該與per相同)我的知識)所以實體需要一段時間才能恢復服務,他們調用服務的save()
方法來保存實體。 Save()
方法只是調用merge()
crud操作。 它與@Transactional
注釋是事務性的。 這是一個問題,當有人拉實體對象並在更改它時,其他人可能會拉動並更改它並將其保存回來,所以我的實體讀取是臟的,如果保存它,我將覆蓋已經更新的實體。 問題是我們在服務之外更改實體並調用save。 這里spring數據存儲庫類是DAO層。
Optimistic lock
是一種解決方案,但由於某些原因我們不喜歡它,所以它對我們不起作用。 我在考慮pessimistic lock
。 例如,當我通過鎖定獲得更新實體時,然后將其更改為位於不同位置的其他位置並回調( entity
已被鎖定以防止更新!)它是否有效? 我不確定它是否仍然是我用於拉實體的EntityManager
對象。 如果它存在,那么在更新並解鎖之前需要很長時間才能通過這些“智能”邏輯。
以下是該場景的簡單示例代碼:
class SomeEntity {
Long id;
String field1;
String field2;
String field3;
String field4;
String field5;
String field6;
String field7;
//getters and setters, column annotations
}
class SomeEntityServiceImple implemenet SomeEntityService{
@Transactional
save(SomeEntity se){
//call to spring data's merge() method
}
get(Long id){
//call to spring data's findOne()
}
}
//this class might be spring bean, might not be. If it is not I will get SomeEntityService bean from shared appContext
class SomeCrazyClass{
@Autowired
SomeEntityService service;
someMethod(){
SomeEntity e = service.get(1L);
//do tons of logic here, change entity object, call some another methods and again, takes 3 seond
service.save(e); //Probably detached and someone updated this object, probably overriding it.
}
}
}
在這里,我無法在服務層內移動那么tons of logic
,它是非常具體的,這種不同的邏輯在100多個地方。
那么有沒有辦法繞過這種情況並至少應用悲觀鎖定?
對於悲觀鎖定,您可以嘗試以下代碼
獲取實體時鎖定
entityManager.find(Entity.class, pk, LockModeType.PESSIMISTIC_WRITE);
之后應用鎖定
entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE);
在查詢中設置鎖定模式
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
否則,您可以嘗試在SomeEntityServiceImpl
添加synchronized方法。
public synchronized void saveEntity(e){
{
Object o = get(id); //-- Fetch most recent entity
applyChanges(o, e); //-- Applying custom changes
save(o); //-- Persisting entity
}
因此,您不必在服務中移動所有邏輯,而只需委派數據庫操作。 此外,它將是代碼更改的常見位置,而不會影響應用程序邏輯。
將@Transactional注釋移動到上層到“SomeCrazyClass”的“someMethod”。 使用此方法所做的更改必須是一個事務流的一部分。 據我所知,這個流應該是原子的,這意味着它應該全部被提交或者不被提交(完全回滾)。
將@Transactional注釋單獨放在退化的保存方法之上是缺少此注釋的要點。 您仍然可以將其保留在那里(使用propagation.required默認值),但您確定需要將其添加到一級。
您應該在整個應用中遵循此建議。 這意味着您應該使用Transactional注釋包裝業務邏輯方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.