2010-04-03 40 views
83

Làm cách nào để tác phẩm chú thích @Version hoạt động trong JPA?Java - JPA - Chú thích @Version

Tôi tìm thấy câu trả lời khác nhau mà chiết xuất được như sau:

JPA sử dụng một phiên bản lĩnh trong các đơn vị của bạn để phát hiện những thay đổi đồng thời vào hồ sơ kho dữ liệu tương tự. Khi thời gian chạy JPA phát hiện một nỗ lực để đồng thời sửa đổi cùng một bản ghi, nó ném một ngoại lệ cho giao dịch cố gắng để cam kết cuối cùng.

Nhưng tôi vẫn không chắc cách thức hoạt động.


Ngoài ra, kể từ những dòng sau:

Bạn nên cân nhắc các lĩnh vực phiên bản không thay đổi. Thay đổi giá trị trường có kết quả không xác định.

Có phải chúng ta nên khai báo trường phiên bản là final?

+0

Tất cả nó làm là kiểm tra/cập nhật các phiên bản trong mỗi truy vấn cập nhật: 'CẬP NHẬT myentity SET mycolumn = 'giá trị mới', phiên bản = phiên bản + 1 WHERE version = [old.version]'. Nếu ai đó cập nhật bản ghi, 'old.version' sẽ không còn khớp với bản ghi trong DB và mệnh đề where sẽ ngăn cập nhật xảy ra. 'Các hàng được cập nhật' sẽ là '0', mà JPA có thể phát hiện để kết luận rằng một sửa đổi đồng thời đã xảy ra. –

Trả lời

145

Nhưng tôi vẫn không chắc nó hoạt động như thế nào?

Giả sử một thực thể MyEntity có một chú thích version tài sản:

@Entity 
public class MyEntity implements Serializable {  

    @Id 
    @GeneratedValue 
    private Long id; 

    private String name; 

    @Version 
    private Long version; 

    //... 
} 

On cập nhật, trường chú thích với @Version sẽ được tăng lên và thêm vào mệnh đề WHERE, một cái gì đó như thế này:

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

Nếu mệnh đề WHERE không khớp với bản ghi (vì cùng một thực thể đã được cập nhật bởi một chủ đề khác), sau đó nhà cung cấp kiên trì sẽ ném một số OptimisticLockException.

Liệu nó có nghĩa rằng chúng ta nên tuyên bố lĩnh vực phiên bản của chúng tôi là cuối cùng

Không nhưng bạn có thể nghĩ tới việc setter bảo vệ như bạn đang không được phép gọi nó.

+5

Tôi nhận được một ngoại lệ null-con trỏ với đoạn mã này, vì khởi tạo dài là null, có lẽ nên được khởi tạo explizitly với 0L. – Markus

+1

Đừng dựa vào tự động mở hộp nó thành 'long' hoặc chỉ gọi' longValue() 'trên đó. Bạn cần kiểm tra 'null' rõ ràng. Tự khởi tạo nó một cách rõ ràng, tôi sẽ không làm như trường này được cho là do nhà cung cấp ORM quản lý. Tôi nghĩ rằng nó được thiết lập để '0L' trên chèn đầu tiên vào DB, vì vậy nếu nó được thiết lập để' null' này nói với bạn hồ sơ này đã không được tiếp tục được nêu ra. –

+0

Điều này có ngăn cản việc tạo một thực thể (cố gắng) được tạo nhiều lần không? Tôi có nghĩa là giả sử một tin nhắn chủ đề JMS kích hoạt việc tạo ra một thực thể và có nhiều trường hợp của một ứng dụng lắng nghe thông báo. Tôi chỉ muốn tránh các lỗi vi phạm ràng buộc duy nhất .. – Manu

8

Mỗi khi thực thể được cập nhật trong cơ sở dữ liệu, trường phiên bản sẽ được tăng thêm một. Mọi hoạt động cập nhật thực thể trong cơ sở dữ liệu sẽ được thêm WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE vào truy vấn của nó. Trong việc kiểm tra các hàng bị ảnh hưởng của hoạt động của bạn, khung công tác jpa có thể đảm bảo không có sửa đổi đồng thời giữa tải và lưu giữ thực thể của bạn vì truy vấn sẽ không tìm thấy thực thể của bạn trong cơ sở dữ liệu khi số phiên bản được tăng giữa tải và tồn tại .

+0

Không thông qua JPAUpdateQuery nó không. Vì vậy, "Mọi hoạt động cập nhật thực thể trong cơ sở dữ liệu sẽ được nối thêm WHERE phiên bản = VERSION_THAT_WAS_LOADED_FROM_DATABASE vào truy vấn của nó" là không đúng sự thật. –

1

Phiên bản được sử dụng để đảm bảo rằng chỉ có một bản cập nhật trong một thời gian. Nhà cung cấp JPA sẽ kiểm tra phiên bản, nếu phiên bản dự kiến ​​đã tăng thì người khác đã cập nhật thực thể để một ngoại lệ sẽ bị ném.

Vì vậy, việc cập nhật giá trị thực thể sẽ an toàn hơn, lạc quan hơn.

Nếu giá trị thay đổi thường xuyên, thì bạn có thể xem xét không sử dụng trường phiên bản. Đối với một ví dụ "một thực thể có lĩnh vực truy cập, mà sẽ tăng mỗi khi một trang web được truy cập"

15

Mặc dù câu trả lời @Pascal là hoàn toàn hợp lệ, từ kinh nghiệm của tôi, tôi tìm mã dưới đây hữu ích để hoàn thành khóa lạc quan:

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

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

    // ... 
} 

Tại sao? Bởi vì:

  1. lạc khóa sẽ không làm việc nếu trường chú thích với @Version được vô tình thiết lập để null.
  2. Vì trường đặc biệt này không nhất thiết phải là phiên bản kinh doanh của đối tượng, để tránh gây hiểu nhầm, tôi thích đặt tên trường đó thành số: optlock thay vì version.

điểm đầu tiên không quan trọng nếu ứng dụng sử dụng chỉ JPA để chèn dữ liệu vào cơ sở dữ liệu, như JPA nhà cung cấp sẽ thi hành 0 cho @version lĩnh vực vào thời điểm sáng tạo. Nhưng hầu như luôn luôn các câu lệnh SQL đơn giản cũng được sử dụng (ít nhất là trong quá trình thử nghiệm đơn vị/tích hợp).

+0

Tôi gọi nó là ** 'u_lmod' ** (người dùng sửa đổi lần cuối) trong đó ** người dùng có thể là một quy trình/thực thể/ứng dụng (tự động) của con người hoặc được xác định (để lưu trữ bổ sung' u_lmod_id' cũng có thể có ý nghĩa). (Theo quan điểm của tôi là một thuộc tính meta-kinh doanh cơ bản). Nó thường lưu trữ giá trị của nó là DATE (TIME) (có nghĩa là thông tin về năm ...millis) trong UTC (nếu múi giờ "vị trí" của trình chỉnh sửa là quan trọng để lưu trữ sau đó với múi giờ). bổ sung rất hữu ích cho các kịch bản đồng bộ DWH. –

+4

Tôi nghĩ rằng bigint thay vì một số nguyên nên được sử dụng trong loại db để phù hợp với một loại java dài. – Dmitry

+0

Điểm tốt Dmitry, cảm ơn, mặc dù khi nói đến phiên bản IMHO nó không thực sự quan trọng. –

0

Chỉ cần thêm một chút thông tin.

JPA quản lý phiên bản dưới mui xe cho bạn, tuy nhiên nó không làm như vậy khi bạn cập nhật bản ghi của mình qua JPAUpdateClause(), trong trường hợp này bạn cần phải thêm số phiên bản vào truy vấn theo cách thủ công.

Pedro

+1

'JPAUpdateClause' là gì? –

Các vấn đề liên quan