2015-11-28 6 views
5

Hazırda bekletme özelliğini kullanarak varlığın sürümünü değiştirmeden veritabanındaki varlığı güncelleyebilir miyim?Belirli bir güncelleştirme için hazırda bekleyen sürüm artışını kapatmak mümkün mü?

Web uygulamamı kullanarak kullanıcılar varlıklar oluşturabilir veya güncelleyebilir. Ve herhangi bir kullanıcı operasyonundan sonra bu varlıkları “işleyen” bir başka asenkron süreçtir. Kullanıcı, varlık “işlendikten” önce güncelleme için bir varlık açarsa, ancak “işlendikten” sonra kaydetmeye çalışırsa, kullanıcı “OptimisticLockException” alır ve girilen tüm veriler kaybolur. Ancak, kullanıcı tarafından sağlanan verilerle senkronize olmayan süreçte güncellenen verilerin üzerine yazmak istiyorum.

//user creates entity by filling form in web application 
Entity entity = new Entity(); 
entity.setValue("some value"); 
entity.setProcessed (false); 
em.persist(entity); 
em.flush(); 
em.clear(); 

//but after short period of time user changes his mind and again opens entity for update 
entity = em.find(Entity.class, entity.getId()); 
em.clear(); //em.clear just for test purposes 

//another application asynchronously updates entities 
List entities = em.createQuery(
       "select e from Entity e where e.processed = false") 
       .getResultList(); 
for (Object o: entities){ 
    Entity entityDb = (Entity)o; 
    someTimeConsumingProcessingOfEntityFields(entityDb); //update lots of diferent entity fields 
    entityDb.setProcessed(true); 
    em.persist(entityDb); 
}   
em.flush(); //version of all processed entities are incremented.  
//Is it possible to prevent version increment? 
em.clear(); 

//user modifies entity in web application and again press "save" button 
em.merge(entity); //em.merge just for test purposes 
entity.setValue("some other value"); 
entity.setProcessed (false); 
em.persist(entityDb); 
em.flush(); //OptimisticLockException will occur.   
//Is it possible to prevent this exception from happening? 
//I would like to overwrite data updated in asynchronous process 
//with user provided data. 

Ve varlık: Ben böyle bir davranış (JPA + hazırda) neden ihtiyaç

program parçası, mikroişlemciyi göstermek için

gerçeklik ben benzer bir sorunu olan çok daha sınıfları var ise
@Entity 
@Table(name = "enities") 
public class Entity implements Serializable { 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 
    @Version 
    private int versionNum; 
    @Column 
    private String value 
    @Column 
    private boolean processed; 
    //… and so on (lots other properties) 
} 

- ben Bazı zarif olmayan müdahaleci çözüm arıyorum.

Bana öyle geliyor ki bu oldukça olağan bir senaryo. Ancak, böyle bir işlevin nasıl elde edileceğine dair herhangi bir bilgi bulamadım.

cevap

0

Hazırda İyimser Kilitleme (Oturum: (http://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/Session.html) çoğaltmak hazırda bekleme kullanılarak atlanabilir ... yöntemi. Çünkü,

//Detaching to prevent hibernate to spot dirty fields. 
//Otherwise if entity exists in hibernate session replication will be skipped 
//and on flush entity will be updated with version increment. 
em.detach(entityDb); 
someTimeConsumingProcessingOfEntityFields(entityDb); 

//Telling hibernate to save without any version modifications. 
//Update hapends only if no newer version exists. 
//Executes additional query DB to get current version of entity. 
hibernateSession.replicate(entity, ReplicationMode.LATEST_VERSION); 

ben bu çözüm yerli SQL güncelleme daha iyi olduğunu düşünüyorum: versiyonunu artırmaz kod

Örnek

  • ORM eşleştirmeleri (anotations veya * .hbm.xml) kullanır (Java nesnelerini çoğaltmaya gerek yok < -> Yerel Tablolarda DB Tabloları eşlemesi);
  • Yıkama işlemini (performans) manuel olarak yürütmeye gerek yoktur; Db ve hazırda bekletme önbellekleri aynı durumdadır, varlıkların önbellek oluşturmasına gerek yoktur (perfomance);
  • ve hala iyimser kilitleme gibi özellikler de dahil olmak üzere tüm ORM özelliklerine sahipsin ...;
0

Sürüm her zaman artar (JPQL kullanmıyorsanız). Bir versiyon fikri, değişiklikleri takip etmektir ve JPA'nın iyimser kilitlemeyi de mümkün kılmasına izin verir, böylece uygulamalar aynı varlıklar ile eşzamanlı olarak çalışabilir.

Ancak, bu soruna geçici bir çözüm bulmak için veritabanından en son sürümü okuyabilir ve kullanıcının üzerinde çalıştığı varlık sürümünden tüm değerleri uygulayabilirsiniz. Böyle

şey:

void updateWithUserInput(Entity userVersion) { 
    Entity dbVersion = entityManager.find(Entity.class, userVersion.getId); 
    // apply changes... 
    dbVersion.setX(userVersion.getX()); 
    // ...and so on 
    // Also: We're dealing with an attached entity so a merge call should not be necessary 
} 

Seni de bunu yapamadı o eskimiş böylece böylece render bu zaman uyumsuz çağrı tarafından gerçekleştirilen tüm değişiklikleri geçersiz olduğunun farkında ve geçersiz gerek olmaz varsayalım herhangi bir değişiklik. :)

+0

Bu çözüm, iyimser kilit denetimini tamamen kapatacak ve birden çok kullanıcı web arayüzünü kullanarak aynı varlığı düzenleyecektir, ancak bunun olmasını istemiyorum. Sorun, kullanıcı öğeyi güncelleştirmez, ancak daha sonra eşzamansız süreç öğeyi günceller. Bu nedenle, bir zaman uyumsuz süreçteki varlık güncellemesini değiştirmem gerektiğini düşünüyorum. – Palladium

+1

Ayrıca, varlığını sürüm özniteliği olmadan farklı bir sınıfa eşleyebilir ve yalnızca async çağrınızda kullanabilirsiniz. – Brian

+0

Evet, bu işe yarayacaktı, ancak maalesef bu çok fazla ilave refactoring gerektiriyordu (alan sınıflarımı tamamen yeniden tasarla). Uygulamamda tüm alan sınıfları (100'den fazla sınıf), alan versionNum ile süper sınıfı genişletir ve bunların önemli bir kısmı bazı asenkron işlemlerle güncellenir. Bu nedenle, belirli bir nedenle alanın değiştirilmesi çok “müdahaleci” bir çözüm olacaktır. Bu durumda, bazı durumlarda (ancak "dallanma" hazırda bekletme anlamına gelir) terfi artışına çevirmek için hazırda varsayılan DefaultFlushEntityEventListener sınıfının üzerine yazmanın daha kolay olacağını düşünüyorum. – Palladium

0

Sen baypas asenkron işlem için bir yerli sorgu güncellemesini yayınlayarak İyimser Kilitleme mekanizmasını Hazırda edebilirsiniz:

em 
    .createNativeQuery("update entities set processed = :1 where id =:2") 
    .setParameter(1, true) 
    .setParameter(2, entityDb.getId()) 
    .executeUpdate(); 
+0

yöntemi someTimeConsumingProcessingOfEntityFields (entityDb), sınıf öğesinin birden çok karmaşık alanını güncelleştirir, bu nedenle tüm bu kodu yerel sorgular kullanarak çoğaltmak oldukça zor olacaktır. (Aslında benzer bir problemle çok daha fazla dersim var - bu yüzden zarif olmayan müdahaleci bir çözüm arıyorum). Ve, yerel sorguları hazırda bekleme ile ikinci sorun, "tutarsız" durumda etki alanı nesnesini bırakır. – Palladium

+0

Ve hazırda bekleme ile yerel sorgular kullanarak ikinci sorun: "tutarsız" durumunda etki alanı nesnesini bırakır (yerel sorgu güncelleştirmeleri etki alanı nesneleri doğrudan DB hazırda bekleme oturumunda farklı nesne değerleri içerecektir). Bu, kod yeniden kullanılabilirliği için sorun olabilir (mevcut nesneleri başka bir yönteme aktaramayacaksınız - önce bunları DB'den güncellemeniz gerekir) – Palladium

İlgili konular