2015-11-28 17 views
5

Có thể cập nhật thực thể trong cơ sở dữ liệu mà không sửa đổi phiên bản của thực thể bằng hibernate không?Có thể tắt tăng phiên bản ngủ đông cho bản cập nhật cụ thể không?

Sử dụng ứng dụng web của tôi mà người dùng có thể tạo hoặc cập nhật đối tượng. Và đâu là một quá trình không đồng bộ khác mà "xử lý" các thực thể này sau bất kỳ thao tác người dùng nào. Nếu người dùng mở thực thể để cập nhật trước khi thực thể được "xử lý", nhưng cố gắng lưu nó sau khi nó được "xử lý", người dùng sẽ nhận được "OptimisticLockException" và tất cả dữ liệu đã nhập của anh ta sẽ bị mất. Nhưng tôi muốn ghi đè lên dữ liệu được cập nhật trong quá trình không đồng bộ với dữ liệu do người dùng cung cấp.

đang snipet để chứng minh lý do tại sao tôi cần phải hành vi như vậy (JPA + Hibernate):

//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. 

Và thực thể của tôi:

@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) 
} 

Trên thực tế tôi đã học nhiều hơn với vấn đề tương tự - vì vậy tôi đang tìm kiếm một số giải pháp thanh lịch không xâm phạm.

Dường như với tôi đây là kịch bản khá bình thường. Nhưng tôi không thể tìm thấy bất kỳ thông tin nào để đạt được chức năng như vậy.

Trả lời

0

Hibernate lạc khóa có thể được bỏ qua sử dụng ngủ đông phiên (http://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/Session.html) lặp lại (...) Phương thức.

Ví dụ về mã mà không tăng phiên bản:

//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); 

Tôi nghĩ rằng giải pháp này là tốt hơn so với bản cập nhật SQL bản địa, bởi vì:

  • ánh xạ ORM (anotations hoặc * .hbm.xml) được sử dụng (không cần phải lặp lại các đối tượng Java < -> Ánh xạ bảng DB trong các truy vấn gốc);
  • không cần phải thực thi bằng tay (hiệu suất);
  • db và hibernate cache nằm trong cùng một trạng thái, không cần phải loại bỏ các thực thể tạo bộ nhớ cache (hiệu suất);
  • và bạn vẫn có tất cả các tính năng được cung cấp ORM như khóa lạc quan, v.v ...;
0

Phiên bản sẽ luôn được tăng lên (trừ khi bạn sử dụng JPQL). Ý tưởng về một phiên bản là theo dõi những thay đổi và trong trường hợp JPA cũng cho phép khóa lạc quan để các ứng dụng có thể làm việc đồng thời với cùng một thực thể.

Nhưng để khắc phục sự cố này, bạn có thể đọc phiên bản mới nhất từ ​​cơ sở dữ liệu và áp dụng tất cả các giá trị từ phiên bản thực thể mà người dùng đã thực hiện.

Something như thế này:

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 
} 

tôi giả sử rằng bạn nhận ra rằng đây loại bỏ bất kỳ thay đổi được thực hiện bởi các cuộc gọi async, do đó làm cho nó trở nên lỗi thời vì vậy bạn cũng không thể làm điều đó và sẽ không cần phải ghi đè bất kỳ thay đổi nào. :)

+0

Giải pháp này sẽ tắt hoàn toàn kiểm tra khóa lạc quan sau đó nhiều người dùng chỉnh sửa cùng một thực thể bằng giao diện web, nhưng tôi không muốn điều đó xảy ra. Vấn đề không phải là sau đó người dùng cập nhật thực thể, nhưng sau đó quá trình không đồng bộ cập nhật thực thể. Vì vậy, tôi nghĩ rằng tôi cần bằng cách nào đó sửa đổi cập nhật của thực thể trong quá trình không đồng bộ. – Palladium

+1

Bạn cũng có thể ánh xạ thực thể đến một lớp khác mà không có thuộc tính phiên bản và chỉ sử dụng nó trong cuộc gọi không đồng bộ của bạn. – Brian

+0

Có, điều đó sẽ có hiệu quả, nhưng tiếc là điều đó đòi hỏi khá nhiều việc tái cấu trúc thêm (thiết kế lại hoàn toàn các lớp miền của tôi). Trong ứng dụng của tôi, tất cả các lớp miền (hơn 100 lớp) mở rộng lớp siêu với phiên bản trườngNếu phần quan trọng của chúng được cập nhật với một số quy trình không đồng bộ. Vì vậy, thay đổi tên miền cho lý do cụ thể này sẽ rất "xâm nhập" giải pháp. Trong trường hợp đó, tôi nghĩ rằng sẽ dễ dàng hơn để ghi đè lên lớp DefaultFlushEntityEventListener hibernate với khả năng biến phiên bản tăng lên trong một số trường hợp (nhưng điều đó có nghĩa là "phân nhánh" ngủ đông). – Palladium

0

Bạn có thể bỏ qua Hibernate cơ chế khóa lạc quan bằng cách phát hành một bản cập nhật truy vấn nguồn gốc để chế biến không đồng bộ:

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

phương thức someTimeConsumingProcessingOfEntityFields (entityDb) cập nhật nhiều trường phức tạp của thực thể lớp vì vậy sẽ rất khó để sao chép tất cả mã này bằng cách sử dụng các truy vấn gốc. (Trong thực tế tôi có nhiều lớp học hơn với vấn đề tương tự - vì vậy tôi đang tìm kiếm một số giải pháp không xâm nhập thanh lịch). Và vấn đề thứ hai với việc sử dụng các truy vấn gốc với hibernate, nó để lại đối tượng miền trong trạng thái "không phù hợp". – Palladium

+0

Và vấn đề thứ hai với việc sử dụng truy vấn gốc với hibernate: nó để lại đối tượng miền trong trạng thái "không nhất quán" (sau khi truy vấn gốc cập nhật đối tượng miền trực tiếp trong phiên làm việc hibernate DB sẽ chứa các giá trị đối tượng khác nhau). Điều này có thể là vấn đề đối với việc sử dụng lại mã (bạn sẽ không thể truyền các đối tượng hiện tại sang phương thức khác - trước hết bạn cần cập nhật chúng từ DB) – Palladium

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