Java - JPA - @Sürüm ek açıklaması


118

@VersionJPA'da ek açıklama nasıl çalışır?

Özü aşağıdaki gibi olan çeşitli cevaplar buldum:

JPA, aynı veri deposu kaydında eşzamanlı değişiklikleri tespit etmek için varlıklarınızdaki bir sürüm alanı kullanır. JPA çalışma zamanı aynı kaydı eşzamanlı olarak değiştirme girişimi algıladığında, en son gerçekleştirmeye çalışan işleme bir istisna atar.

Ama hala nasıl çalıştığından emin değilim.


Ayrıca aşağıdaki satırlarda olduğu gibi:

Sürüm alanlarının değişmez olduğunu düşünmelisiniz. Alan değerinin değiştirilmesi tanımlanmamış sonuçlara neden olur.

Sürüm alanımızı olarak ilan etmemiz gerektiği anlamına mı geliyor final?


1
Öyle şey çek / güncelleme her güncelleme sorguda sürümü: UPDATE myentity SET mycolumn = 'new value', version = version + 1 WHERE version = [old.version]. Birisi kaydı güncellediyse, old.versionartık DB'deki ile eşleşmeyecek ve where cümlesi güncellemenin olmasını engelleyecektir. 0JPA'nın eşzamanlı bir değişikliğin meydana geldiği sonucuna varmak için algılayabileceği 'satırlar güncellenir' olacaktır .
Stijn de Witt

Yanıtlar:


189

Ama yine de nasıl çalıştığından emin değilim?

Bir varlığın MyEntityek açıklamalı bir versionmülke sahip olduğunu varsayalım :

@Entity
public class MyEntity implements Serializable {    

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @Version
    private Long version;

    //...
}

Güncellemede, ile not verilen alan @Versionartırılacak ve aşağıdaki WHEREgibi maddeye eklenecektir :

UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))

Eğer WHEREmaddesinin (aynı varlık zaten başka bir iş parçacığı tarafından güncellendi çünkü) kaydını eşleştirememişse ardından kalıcılık sağlayıcısı bir atacağım OptimisticLockException.

Bu, sürüm alanımızı nihai olarak ilan etmemiz gerektiği anlamına mı geliyor?

Hayır, ama pasör dememen gerektiği gibi korumayı düşünebilirsin.


5
Bu kod parçasıyla bir boş işaretçi istisnası buldum, çünkü Long initialises null olarak muhtemelen 0L ile başlatılmalıdır.
Markus

2
Kutusunu otomatik olarak açmaya longveya sadece çağırmaya güvenmeyin longValue(). Açık nullkontrollere ihtiyacınız var . Bunu açıkça kendiniz başlatmanız, bu alanın ORM sağlayıcısı tarafından yönetilmesi gerektiği için yapmam. Bence 0LDB'ye ilk girişte ayarlanacak , bu yüzden buna ayarlanmışsa null, size bu kaydın henüz kalıcı olmadığını söyler.
Stijn de Witt

Bu, bir varlığın birden fazla kez yaratılmasını (denenmesini) engelleyecek mi? Demek istediğim, bir JMS konu mesajının bir varlığın oluşturulmasını tetiklediğini ve bir uygulamanın mesajı dinleyen birden fazla örneği olduğunu varsayalım. Sadece benzersiz kısıtlama ihlali hatasından kaçınmak istiyorum ..
Manu

Üzgünüm, bunun eski bir iş parçacığı olduğunun farkındayım, ancak @ Sürüm kümelenmiş ortamlarda kirli önbelleği de dikkate alıyor mu?
Shawn Eion Smith

3
Spring Data JPA kullanılıyorsa , alan için Longbir ilkelden ziyade bir sarmalayıcı kullanmak daha iyi olabilir , çünkü büyük olasılıkla boş bir sürüm alanını varlığın yeni olduğuna dair bir gösterge olarak değerlendirecektir. Varlık yeniyse (sürümün boş olmasıyla belirtildiği gibi), Spring arayacaktır . Ancak sürümün bir değeri varsa, o zaman arayacaktır . Bu aynı zamanda bir sarmalayıcı türü olarak bildirilen bir sürüm alanının başlatılmamış bırakılmasının nedenidir. long@VersionSimpleJpaRepository.save(entity)em.persist(entity)em.merge(entity)
rdguam

33

@Pascal cevabı tamamen geçerli olsa da, deneyimlerime göre aşağıdaki kodu iyimser kilitlemeyi başarmak için yararlı buluyorum:

@Entity
public class MyEntity implements Serializable {    
    // ...

    @Version
    @Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false)
    private long version = 0L;

    // ...
}

Neden? Çünkü:

  1. İle not verilen alan yanlışlıkla olarak ayarlanırsa iyimser kilitleme çalışmaz .@Versionnull
  2. Bu özel alan, mutlaka nesnenin bir iş versiyonu olmadığından, yanıltıcı bir optlockdurumdan kaçınmak için, bu alanı yerine benzeri bir şeyle adlandırmayı tercih ederim version.

JPA satıcısı oluşturma sırasında alan için zorlayacağından, uygulamanın veri tabanına veri eklemek için yalnızca JPA kullanması önemli değildir . Ancak neredeyse her zaman düz SQL ifadeleri de kullanılmaktadır (en azından birim ve entegrasyon testi sırasında).0@version


Ben bunu u_lmod(kullanıcının son değiştirdiği) bir insan veya tanımlanmış (otomatikleştirilmiş) süreç / varlık / uygulamau_lmod_id olabileceği yerde (böylece ek olarak depolamak da mantıklı olabilir ) diyorum . (Benim görüşüme göre çok temel bir ticari meta niteliğidir). Tipik olarak değerini DATE (TIME) (yani yıl hakkında bilgi ... milisaniye) olarak UTC'de saklar (eğer düzenleyicinin zaman dilimi "konumu" o zaman dilimiyle birlikte saklamak için önemliyse). ayrıca DWH senkronizasyon senaryoları için çok kullanışlıdır.
Andreas Dietrich

7
Uzun bir java türü ile eşleşmek için db türünde bir tamsayı yerine bigint kullanılması gerektiğini düşünüyorum.
Dmitry

İyi nokta Dmitry, teşekkürler, IMHO versiyonunu oluşturmaya gelince gerçekten önemli değil.
G. Demecki

9

Veritabanında bir varlık her güncellendiğinde, sürüm alanı bir artırılacaktır. Veritabanındaki varlığı güncelleyen her işlem WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE, sorgusuna eklenecektir .

İşleminizin etkilenen satırlarını kontrol ederken jpa çerçevesi, varlığınızı yükleme ve sürdürme arasında eşzamanlı bir değişiklik olmadığından emin olabilir, çünkü sorgu, yükleme ve kalıcılık arasında sürüm numarası artırıldığında veritabanında varlığınızı bulamaz.


JPAUpdateQuery aracılığıyla değil. Dolayısıyla, "Veritabanındaki varlığı güncelleyen her işlem, sorgusuna WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE ekleyecektir" doğru değildir.
Pedro Borges

2

Bir seferde yalnızca bir güncelleme olmasını sağlamak için kullanılan sürüm. JPA sağlayıcısı sürümü kontrol edecek, eğer beklenen sürüm zaten artmışsa, başka biri varlığı zaten güncelleyerek bir istisna atılacaktır.

Dolayısıyla, varlık değerini güncellemek daha güvenli ve daha iyimser olacaktır.

Değer sık ​​sık değişirse, sürüm alanını kullanmamayı düşünebilirsiniz. Bir örnek için "sayaç alanı olan ve bir web sayfasına her erişildiğinde artacak bir varlık"


0

Sadece biraz daha bilgi ekledim.

JPA, sizin için başlık altındaki sürümü yönetir, ancak kaydınızı güncellediğinizde bunu yapmaz JPAUpdateClause, bu gibi durumlarda sürüm artışını sorguya manuel olarak eklemeniz gerekir.

Aynı şey JPQL yoluyla güncelleme için de söylenebilir, yani varlıkta basit bir değişiklik değil, ancak hazırda bekletme ile yapılmış olsa bile veritabanına bir güncelleme komutu.

Pedro


3
Nedir JPAUpdateClause?
Karl Richter

İyi soru, basit cevap: kötü örnek :) JPAUpdateClause, QueryDSL'ye özgüdür, koddaki bir varlığın durumunu basitçe etkilemek yerine JPQL ile bir güncelleme çalıştırmayı düşünün.
Pedro Borges
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.