JPA EntityManager: Tại sao nên sử dụng contin () over merge ()?


951

EntityManager.merge() có thể chèn các đối tượng mới và cập nhật những đối tượng hiện có.

Tại sao một người muốn sử dụng persist()(chỉ có thể tạo các đối tượng mới)?



2
Nếu bạn thích sơ đồ. Tham khảo điều này: spitballer.blogspot.in/2010/04/ Cách
RBz

Câu trả lời:


1615

Dù bằng cách nào cũng sẽ thêm một thực thể vào PersistenceContext, sự khác biệt là ở những gì bạn làm với thực thể sau đó.

Persist lấy một thực thể, thêm nó vào ngữ cảnh và làm cho cá thể đó được quản lý (tức là các cập nhật trong tương lai cho thực thể sẽ được theo dõi).

Hợp nhất trả về thể hiện được quản lý mà trạng thái được hợp nhất. Nó trả về một cái gì đó tồn tại trong PersistenceContext hoặc tạo một thể hiện mới của thực thể của bạn. Trong mọi trường hợp, nó sẽ sao chép trạng thái từ thực thể được cung cấp và trả về bản sao được quản lý. Trường hợp bạn chuyển vào sẽ không được quản lý (mọi thay đổi bạn thực hiện sẽ không phải là một phần của giao dịch - trừ khi bạn gọi lại hợp nhất). Bạn có thể thông qua việc sử dụng thể hiện trả lại (được quản lý).

Có lẽ một ví dụ mã sẽ giúp.

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

Kịch bản 1 và 3 tương đương nhau, nhưng có một số tình huống bạn muốn sử dụng Kịch bản 2.


3
@dma_k: Có vẻ như bạn đang sử dụng Hibernate. Tôi ít quen thuộc với Hibernate hơn JPA - nhưng trong JPA nếu bạn gọi EntityManager.persist () và chuyển qua một thực thể tách rời, bạn sẽ: a) nhận EntityExistsException ngay lập tức hoặc b) nhận một PersistenceException khác trong thời gian xả / cam kết. Có lẽ tôi đã hiểu nhầm câu hỏi ở đây?
Mike

49
Câu trả lời này có thể được cải thiện nếu nó cũng bao gồm các trường hợp thực thể được hợp nhất / tồn tại đã tồn tại trong bối cảnh liên tục (hoặc ít nhất làm rõ hơn rằng nó chỉ mô tả hành vi khi thực thể tồn tại / sáp nhập không tồn tại)
Henry

2
Là một phương pháp hiệu quả hơn? Có lẽ mergebản sao đầy đủ của một đối tượng trước khi quản lý nó có hiệu năng?
Kevin Meredith

2
Còn id thì sao? Nếu tôi có tôi có @GeneratedIdthể lấy nó trong kịch bản 2 không?
rascio

7
Mike: "Hợp nhất tạo một trường hợp mới ...": điều này không phải lúc nào cũng đúng. Nếu EntityManager tìm thấy một thực thể đã được quản lý trong ngữ cảnh của nó, nó sẽ trả về thể hiện này (sau khi đã cập nhật các trường). Vui lòng chỉnh sửa câu trả lời của bạn, sau đó tôi sẽ bỏ phiếu cho nó.
Heri

181

Kiên trì và hợp nhất là cho hai mục đích khác nhau (chúng không phải là sự thay thế nào cả).

(chỉnh sửa để mở rộng thông tin khác biệt)

kiên trì:

  • Chèn một thanh ghi mới vào cơ sở dữ liệu
  • Đính kèm đối tượng cho người quản lý thực thể.

hợp nhất:

  • Tìm một đối tượng đính kèm với cùng id và cập nhật nó.
  • Nếu tồn tại cập nhật và trả về đối tượng đã đính kèm.
  • Nếu không tồn tại, chèn thanh ghi mới vào cơ sở dữ liệu.

hiệu quả ()

  • Nó có thể hiệu quả hơn khi chèn một thanh ghi mới vào cơ sở dữ liệu hơn là hợp nhất ().
  • Nó không trùng lặp đối tượng ban đầu.

kiên trì () ngữ nghĩa:

  • Nó đảm bảo rằng bạn đang chèn và không cập nhật do nhầm lẫn.

Thí dụ:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

Cách này chỉ tồn tại 1 đối tượng đính kèm cho bất kỳ đăng ký nào trong trình quản lý thực thể.

merge () cho một thực thể với id là một cái gì đó như:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

Mặc dù nếu được kết nối với MySQL merge () có thể hiệu quả như vẫn tồn tại () bằng cách sử dụng lệnh gọi tới INSERT với tùy chọn ON DUPLICATE KEY UPDATE, JPA là một chương trình cấp cao và bạn không thể cho rằng điều này sẽ xảy ra ở mọi nơi.


Bạn có thể đặt tên cho một trường hợp nó không có giá trị để thay thế em.persist(x)với x = em.merge(x)?
Aaron Digulla

20
kiên trì () có thể ném EntityExistsException. Nếu bạn muốn chắc chắn rằng mã của bạn đang thực hiện thao tác chèn chứ không phải cập nhật dữ liệu bạn phải sử dụng liên tục.
Josep Panadero

1
merge()cũng có thể némEntityExistsException
Sean

1
@ Không thể bởi vì nó là một RuntimeException, nhưng nó không được đề cập trong Javadoc.
Martin

154

Nếu bạn đang sử dụng trình tạo được chỉ định, sử dụng hợp nhất thay vì liên tục có thể gây ra câu lệnh SQL dư thừa , do đó ảnh hưởng đến hiệu suất.

Ngoài ra, gọi hợp nhất cho các thực thể được quản lý cũng là một sai lầm vì các thực thể được quản lý được quản lý tự động bởi Hibernate và trạng thái của chúng được đồng bộ hóa với bản ghi cơ sở dữ liệu bằng cơ chế kiểm tra bẩn khi xóa Bối cảnh liên tục .

Để hiểu cách thức hoạt động của tất cả những điều này, trước tiên bạn nên biết rằng Hibernate chuyển tư duy của nhà phát triển từ các câu lệnh SQL sang các chuyển đổi trạng thái thực thể .

Khi một thực thể được Hibernate quản lý tích cực, tất cả các thay đổi sẽ được tự động truyền tới cơ sở dữ liệu.

Màn hình ngủ đông hiện đang gắn liền với các thực thể. Nhưng để một thực thể trở nên được quản lý, nó phải ở trạng thái thực thể phù hợp.

Để hiểu rõ hơn về chuyển đổi trạng thái JPA, bạn có thể hình dung sơ đồ sau:

Chuyển trạng thái thực thể JPA

Hoặc nếu bạn sử dụng API cụ thể Hibernate:

Chuyển trạng thái thực thể ngủ đông

Như được minh họa bởi các sơ đồ trên, một thực thể có thể ở một trong bốn trạng thái sau:

  • Mới (thoáng qua)

    Một đối tượng mới được tạo chưa từng được liên kết với Hibernate Session(aka Persistence Context) và không được ánh xạ tới bất kỳ hàng trong bảng cơ sở dữ liệu nào được coi là ở trạng thái Mới (Tạm thời).

    Để trở nên bền bỉ, chúng ta cần gọi EntityManager#persistphương thức một cách rõ ràng hoặc sử dụng cơ chế duy trì bắc cầu.

  • Kiên trì (Quản lý)

    Một thực thể liên tục đã được liên kết với một hàng của bảng cơ sở dữ liệu và nó được quản lý bởi Bối cảnh liên tục hiện đang chạy. Bất kỳ thay đổi nào được thực hiện cho một thực thể như vậy sẽ được phát hiện và lan truyền đến cơ sở dữ liệu (trong thời gian tuôn ra Phiên). Với Hibernate, chúng ta không còn phải thực thi các câu lệnh INSERT / UPDATE / DELETE. Hibernate sử dụng một phong cách làm việc đằng sau giao dịch và các thay đổi được đồng bộ hóa tại thời điểm chịu trách nhiệm cuối cùng, trong thời gian Sessionxả hiện tại .

  • Tách rời

    Khi Bối cảnh liên tục hiện đang chạy, tất cả các thực thể được quản lý trước đó sẽ bị tách ra. Những thay đổi kế tiếp sẽ không còn được theo dõi và sẽ không xảy ra đồng bộ hóa cơ sở dữ liệu tự động.

    Để liên kết một thực thể tách rời với Phiên Hibernate đang hoạt động, bạn có thể chọn một trong các tùy chọn sau:

    • Gắn lại

      Hibernate (nhưng không phải JPA 2.1) hỗ trợ gắn lại thông qua phương thức cập nhật Phiên #. Phiên Hibernate chỉ có thể liên kết một đối tượng Thực thể cho một hàng cơ sở dữ liệu đã cho. Điều này là do Bối cảnh liên tục hoạt động như một bộ đệm trong bộ nhớ (bộ đệm cấp đầu tiên) và chỉ có một giá trị (thực thể) được liên kết với một khóa nhất định (loại thực thể và định danh cơ sở dữ liệu). Một thực thể chỉ có thể được gắn lại nếu không có đối tượng JVM khác (khớp với cùng một hàng cơ sở dữ liệu) đã được liên kết với Phiên Hibernate hiện tại.

    • Sáp nhập

    Việc hợp nhất sẽ sao chép trạng thái thực thể tách rời (nguồn) sang một thực thể được quản lý (đích). Nếu thực thể hợp nhất không có tương đương trong Phiên hiện tại, một thực thể sẽ được tìm nạp từ cơ sở dữ liệu. Đối tượng được tách ra sẽ tiếp tục được tách ra ngay cả sau khi hoạt động hợp nhất.

  • Đã xóa

    Mặc dù JPA yêu cầu các thực thể được quản lý chỉ được phép xóa, Hibernate cũng có thể xóa các thực thể tách rời (nhưng chỉ thông qua lệnh gọi phương thức xóa Phiên #). Một thực thể bị xóa chỉ được lên lịch để xóa và câu lệnh XÓA cơ sở dữ liệu thực tế sẽ được thực thi trong thời gian xả phiên.



@gstackoverflow Câu trả lời bạn nhận được là câu trả lời đúng. Để biết thêm chi tiết, hãy xem bài viết này hoặc cuốn sách của tôi, Tính bền vững của Java hiệu suất cao .
Vlad Mihalcea

Do đó, không có khả năng thay đổi thứ tự hoạt động cho orphanremoval = true?
gstackoverflow

Bài viết của bạn về thứ tự hoạt động trong trường hợp thông thường. Câu hỏi của tôi cụ thể cho orphanRemoval
gstackoverflow

1
Thực tế là không thể giải thích ngủ đông bằng một sơ đồ như thế. Tại sao bạn không thể tuôn ra phiên sau khi tách ra? Điều gì xảy ra khi bạn cố gắng lưu một thực thể đã tồn tại? Tại sao hành vi xả nước khác nhau khi nói đến tiết kiệm và tồn tại? Có 1000 câu hỏi như vậy, mà không ai có logic rõ ràng.
GingerBeer

37

Tôi nhận thấy rằng khi tôi sử dụng em.merge, tôi nhận được một SELECTtuyên bố cho mọi người INSERT, ngay cả khi không có trường nào mà JPA đang tạo cho tôi - trường khóa chính là UUID do tôi tự đặt. Tôi chuyển sang em.persist(myEntityObject)và chỉ nhận được INSERTbáo cáo sau đó.


3
Có ý nghĩa kể từ khi bạn gán ID và bộ chứa JPA không biết bạn lấy từ đâu. Có một cơ hội (nhỏ) rằng đối tượng đã tồn tại trong cơ sở dữ liệu, ví dụ như trong một kịch bản trong đó một số ứng dụng ghi vào cùng một cơ sở dữ liệu.
Aaron Digulla

Tôi đã đối mặt với vấn đề tương tự với merge(). Tôi đã có cơ sở dữ liệu PostgreSQL với chế độ xem phức tạp : chế độ xem tổng hợp dữ liệu từ một số bảng (các bảng có cấu trúc giống hệt nhau nhưng tên khác nhau). Vì vậy, JPA đã cố gắng thực hiện merge(), nhưng thực tế JPA đã tạo trước tiên SELECT(cơ sở dữ liệu do cài đặt chế độ xem có thể trả về một số bản ghi có cùng khóa chính từ các bảng khác nhau!), Sau đó JPA (Hibernate là một triển khai) đã thất bại: có một số bản ghi có cùng khóa ( org.hibernate.HibernateException: More than one row with the given identifier was found). Trong trường hợp của tôi persist()đã giúp tôi.
flaz14

29

Các đặc điểm kỹ thuật JPA nói sau đây về persist().

Nếu X là một đối tượng tách rời, EntityExistsExceptioncó thể được ném khi hoạt động bền bỉ được gọi, hoặc EntityExistsExceptionhoặc một đối tượng khác PersistenceExceptioncó thể được ném vào lúc xả hoặc cam kết.

Vì vậy, việc sử dụng persist()sẽ phù hợp khi đối tượng không phải là một đối tượng tách rời. Bạn có thể muốn có mã ném PersistenceExceptionđể nó thất bại nhanh chóng.

Mặc dù thông số kỹ thuật không rõ ràng , nhưng persist()có thể đặt @GeneratedValue @Idcho một đối tượng. merge()tuy nhiên phải có một đối tượng với @Idđã được tạo.


5
+1 cho " merge()tuy nhiên phải có một đối tượng với @Id đã được tạo . ". Bất cứ khi nào EntityManager không tìm thấy giá trị cho trường của ID đối tượng, nó vẫn tồn tại (được chèn) vào DB.
Omar

Tôi đã không hiểu điều này đầu tiên vì tôi không rõ ràng về các tiểu bang. Hy vọng điều này sẽ giúp ai đó như nó đã làm cho tôi. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/iêu
RBz

1
@GeneratedValue không có ý nghĩa khác nhau cho hợp nhất () và bền vững ()
SandeepGodara

17

Một số chi tiết khác về hợp nhất sẽ giúp bạn sử dụng hợp nhất trên liên tục:

Trả về một thể hiện được quản lý khác với thực thể ban đầu là một phần quan trọng của quy trình hợp nhất. Nếu một thực thể có cùng định danh đã tồn tại trong ngữ cảnh bền vững, nhà cung cấp sẽ ghi đè trạng thái của nó với trạng thái của thực thể được hợp nhất, nhưng phiên bản được quản lý tồn tại phải được trả về cho khách hàng để có thể được trả về đã sử dụng. Nếu nhà cung cấp không cập nhật phiên bản Nhân viên trong ngữ cảnh liên tục, mọi tham chiếu đến trường hợp đó sẽ trở nên không nhất quán với trạng thái mới được sáp nhập.

Khi merge () được gọi trên một thực thể mới, nó hoạt động tương tự như hoạt động bền vững (). Nó thêm thực thể vào bối cảnh bền vững, nhưng thay vì thêm thể hiện thực thể ban đầu, nó tạo ra một bản sao mới và thay vào đó quản lý thể hiện đó. Bản sao được tạo bởi hoạt động merge () được duy trì như thể phương thức contin () được gọi trên nó.

Khi có các mối quan hệ, hoạt động merge () sẽ cố gắng cập nhật thực thể được quản lý để trỏ đến các phiên bản được quản lý của các thực thể được tham chiếu bởi thực thể tách rời. Nếu thực thể có mối quan hệ với một đối tượng không có danh tính liên tục, kết quả của hoạt động hợp nhất là không xác định. Một số nhà cung cấp có thể cho phép bản sao được quản lý trỏ đến đối tượng không liên tục, trong khi những nhà cung cấp khác có thể ném ngoại lệ ngay lập tức. Hoạt động hợp nhất () có thể được xếp tầng tùy ý trong các trường hợp này để ngăn chặn ngoại lệ xảy ra. Chúng tôi sẽ đề cập đến tầng của hoạt động merge () sau trong phần này. Nếu một thực thể được hợp nhất trỏ đến một thực thể bị xóa, một ngoại lệ IllegalArgumentException sẽ bị ném.

Mối quan hệ tải nhanh là một trường hợp đặc biệt trong hoạt động hợp nhất. Nếu mối quan hệ tải lười biếng không được kích hoạt trên một thực thể trước khi nó bị tách ra, mối quan hệ đó sẽ bị bỏ qua khi thực thể được hợp nhất. Nếu mối quan hệ được kích hoạt trong khi được quản lý và sau đó được đặt thành null trong khi thực thể được tách ra, phiên bản được quản lý của thực thể cũng sẽ xóa mối quan hệ trong quá trình hợp nhất. "

Tất cả các thông tin trên được lấy từ "Pro JPA 2 Làm chủ API bền vững Java ™" của Mike Keith và Merrick Schnicariol. Chương 6. Phần tách ra và sáp nhập. Cuốn sách này thực sự là một cuốn sách thứ hai dành cho JPA của các tác giả. Cuốn sách mới này có nhiều thông tin mới sau đó cũ. Tôi thực sự khuyên bạn nên đọc cuốn sách này cho những người sẽ tham gia nghiêm túc với JPA. Tôi xin lỗi vì đã vô tình đăng câu trả lời đầu tiên của tôi.


17

Có một số khác biệt nữa giữa mergepersist(tôi sẽ liệt kê lại những thứ đã được đăng ở đây):

D1. mergekhông làm cho thực thể được thông qua được quản lý, mà trả về một thể hiện khác được quản lý. persistở phía bên kia sẽ làm cho thực thể được thông qua được quản lý:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

Đ2. Nếu bạn xóa một thực thể và sau đó quyết định duy trì thực thể đó trở lại, bạn có thể làm điều đó chỉ với kiên trì (), vì mergesẽ ném một thực thể IllegalArgumentException.

D3. Nếu bạn quyết định chăm sóc ID thủ công của mình (ví dụ: bằng cách sử dụng UUID), thì một merge thao tác sẽ kích hoạt các SELECTtruy vấn tiếp theo để tìm kiếm các thực thể tồn tại với ID đó, trong khi persistcó thể không cần các truy vấn đó.

Đ4. Có những trường hợp khi bạn không tin tưởng mã gọi mã của mình và để đảm bảo rằng không có dữ liệu nào được cập nhật mà thay vào đó, bạn phải sử dụng persist.


8

Tôi đã nhận được các trường hợp ngoại lệ lười biếng trên thực thể của mình bởi vì tôi đang cố gắng truy cập vào một bộ sưu tập được tải lười biếng trong phiên.

Những gì tôi sẽ làm là trong một yêu cầu riêng biệt, truy xuất thực thể từ phiên và sau đó thử truy cập vào một bộ sưu tập trong trang jsp của tôi có vấn đề.

Để giảm bớt điều này, tôi đã cập nhật cùng một thực thể trong bộ điều khiển của mình và chuyển nó vào jsp của tôi, mặc dù tôi tưởng tượng khi tôi lưu lại trong phiên rằng nó cũng sẽ có thể truy cập được SessionScopevà không ném LazyLoadingException, một sửa đổi của ví dụ 2:

Sau đây đã làm việc cho tôi:

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!

7

Tôi tìm thấy lời giải thích này từ các tài liệu Hibernate khai sáng, bởi vì chúng có chứa một trường hợp sử dụng:

Việc sử dụng và ngữ nghĩa của merge () dường như gây nhầm lẫn cho người dùng mới. Thứ nhất, miễn là bạn không cố gắng sử dụng trạng thái đối tượng được tải trong một trình quản lý thực thể trong một trình quản lý thực thể mới khác, bạn không cần phải sử dụng merge () . Một số ứng dụng toàn bộ sẽ không bao giờ sử dụng phương pháp này.

Thông thường merge () được sử dụng trong kịch bản sau:

  • Ứng dụng tải một đối tượng trong trình quản lý thực thể đầu tiên
  • đối tượng được chuyển lên lớp trình bày
  • một số sửa đổi được thực hiện cho đối tượng
  • đối tượng được chuyển trở lại lớp logic nghiệp vụ
  • ứng dụng vẫn duy trì các sửa đổi này bằng cách gọi merge () trong trình quản lý thực thể thứ hai

Đây là ngữ nghĩa chính xác của merge ():

  • nếu có một cá thể được quản lý với cùng một mã định danh hiện được liên kết với bối cảnh bền vững, hãy sao chép trạng thái của đối tượng đã cho vào đối tượng được quản lý
  • nếu không có phiên bản được quản lý hiện được liên kết với bối cảnh bền vững, hãy thử tải nó từ cơ sở dữ liệu hoặc tạo một phiên bản được quản lý mới
  • cá thể được quản lý được trả về
  • trường hợp cụ thể không trở nên gắn liền với bối cảnh tồn tại, nó vẫn bị tách ra và thường bị loại bỏ

Từ: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html


6

Đi qua các câu trả lời, có một số chi tiết còn thiếu liên quan đến 'Cascade' và thế hệ id. Xem câu hỏi

Ngoài ra, điều đáng nói là bạn có thể có các Cascadechú thích riêng để hợp nhất và tồn tại: Cascade.MERGECascade.PERSISTsẽ được xử lý theo phương pháp được sử dụng.

Thông số kỹ thuật là bạn của bạn;)


6

JPA không thể chối cãi là một sự đơn giản hóa lớn trong miền ứng dụng doanh nghiệp được xây dựng trên nền tảng Java. Là một nhà phát triển đã phải đối phó với sự phức tạp của các thực thể cũ trong J2EE, tôi thấy việc đưa JPA vào các đặc tả Java EE là một bước tiến lớn. Tuy nhiên, trong khi tìm hiểu sâu hơn về các chi tiết của JPA, tôi thấy những điều không dễ dàng như vậy. Trong bài viết này, tôi đề cập đến việc so sánh các phương thức hợp nhất và liên tục của EntityManuber mà hành vi chồng chéo của họ có thể gây nhầm lẫn không chỉ với người mới. Hơn nữa, tôi đề xuất một khái quát hóa xem cả hai phương pháp là trường hợp đặc biệt của một phương pháp tổng quát hơn kết hợp.

Thực thể bền bỉ

Ngược lại với phương thức hợp nhất, phương thức kiên trì khá đơn giản và trực quan. Kịch bản phổ biến nhất về việc sử dụng phương thức kiên trì có thể được tóm tắt như sau:

"Một thể hiện mới được tạo của lớp thực thể được truyền cho phương thức kiên trì. Sau khi phương thức này trả về, thực thể được quản lý và lên kế hoạch để chèn vào cơ sở dữ liệu. Nó có thể xảy ra tại hoặc trước khi giao dịch được thực hiện hoặc khi phương thức tuôn ra được gọi. Nếu thực thể tham chiếu đến một thực thể khác thông qua mối quan hệ được đánh dấu bằng chiến lược xếp tầng PERSIST thì quy trình này cũng được áp dụng cho thực thể đó. "

nhập mô tả hình ảnh ở đây

Thông số kỹ thuật đi sâu vào chi tiết, tuy nhiên, việc ghi nhớ chúng là không quan trọng vì những chi tiết này chỉ bao gồm ít nhiều tình huống kỳ lạ.

Sáp nhập thực thể

So với vẫn tồn tại, việc mô tả hành vi của hợp nhất không đơn giản như vậy. Không có kịch bản chính, vì trong trường hợp vẫn tồn tại và một lập trình viên phải nhớ tất cả các kịch bản để viết mã chính xác. Dường như với tôi, các nhà thiết kế JPA muốn có một số phương thức mà mối quan tâm chính của họ sẽ là xử lý các thực thể tách rời (ngược lại với phương thức bền vững liên quan đến các thực thể mới được tạo ra chủ yếu.) Nhiệm vụ chính của phương thức hợp nhất là chuyển trạng thái từ một thực thể không được quản lý (được chuyển làm đối số) cho đối tác được quản lý của nó trong bối cảnh tồn tại. Tuy nhiên, nhiệm vụ này phân chia thành nhiều tình huống làm xấu đi tính dễ hiểu của hành vi của phương pháp tổng thể.

Thay vì lặp lại các đoạn từ đặc tả JPA, tôi đã chuẩn bị một sơ đồ dòng mô tả sơ đồ hành vi của phương thức hợp nhất:

nhập mô tả hình ảnh ở đây

Vì vậy, khi nào tôi nên sử dụng liên tục và khi hợp nhất?

kiên trì

  • Bạn muốn phương thức luôn tạo một thực thể mới và không bao giờ cập nhật một thực thể. Mặt khác, phương thức đưa ra một ngoại lệ do hậu quả của vi phạm tính duy nhất của khóa chính.
  • Xử lý hàng loạt, xử lý các thực thể một cách có trạng thái (xem mẫu Cổng).
  • Tối ưu hóa hiệu suất

hợp nhất

  • Bạn muốn phương thức chèn hoặc cập nhật một thực thể trong cơ sở dữ liệu.
  • Bạn muốn xử lý các thực thể theo cách không trạng thái (đối tượng truyền dữ liệu trong dịch vụ)
  • Bạn muốn chèn một thực thể mới có thể có tham chiếu đến một thực thể khác có thể nhưng chưa được tạo (mối quan hệ phải được đánh dấu MERGE). Ví dụ: chèn ảnh mới có tham chiếu đến album mới hoặc album có sẵn.

Sự khác biệt giữa E được quản lý và PC có chứa phiên bản được quản lý của E không?
GingerBeer

5

Kịch bản X:

Bảng: Spitter (Một), Bảng: Spittles (Nhiều) (Spittles là Chủ sở hữu của mối quan hệ với FK: spitter_id)

Kịch bản này dẫn đến việc tiết kiệm: Spitter và cả Spittles như thể được sở hữu bởi Same Spitter.

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

Kịch bản Y:

Điều này sẽ cứu Spitter, sẽ cứu 2 Spittles Nhưng họ sẽ không tham chiếu cùng Spitter!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

1
Spitter là một đối tượng được lấy từ cuốn sách "Spring in Action" phiên bản thứ ba của Graig Walls. Spitters là những người nói điều gì đó và Spittle của họ là những gì họ thực sự đang nói. Vì vậy, một Spitter có nhiều spittles có nghĩa là anh ta có một danh sách các Chuỗi.
George Papeditodorou

1
Bạn có thể đã sử dụng một ví dụ dễ đọc hơn một chút mà không cần đọc Spring in Action ...
wonderb0lt

1
Bạn thực sự không cần biết spittle hay spitter là gì vì trên đầu có ghi rằng Spitter là một cái bàn, spitter là một cái bàn khác sở hữu .. cái này và cái kia ...
George Papeditodorou

3

Một quan sát khác:

merge()sẽ chỉ quan tâm đến một id được tạo tự động (đã được thử nghiệm IDENTITYSEQUENCE) khi một bản ghi với id đó đã tồn tại trong bảng của bạn. Trong trường hợp đó merge()sẽ cố gắng cập nhật hồ sơ. Tuy nhiên, nếu một id vắng mặt hoặc không khớp với bất kỳ bản ghi hiện có nào, merge()sẽ hoàn toàn bỏ qua nó và yêu cầu db phân bổ một bản ghi mới. Điều này đôi khi là một nguồn của rất nhiều lỗi. Không sử dụng merge()để buộc id cho một bản ghi mới.

persist()mặt khác sẽ không bao giờ cho phép bạn thậm chí chuyển một id cho nó. Nó sẽ thất bại ngay lập tức. Trong trường hợp của tôi, đó là:

Nguyên nhân bởi: org.hibernate.PersistentObjectException: thực thể tách rời được truyền để tồn tại

hibernate-jpa javadoc có một gợi ý:

Ném : javax.persistence.EntityExistsException - nếu thực thể đã tồn tại. (Nếu thực thể đã tồn tại, EntityExistsException có thể bị ném khi hoạt động liên tục được gọi, hoặc EntityExistsException hoặc một PersistenceException khác có thể được ném vào lúc xả hoặc cam kết.)


2
Bạn không sử dụng ID được tạo tự động, bạn phải cung cấp ID thực thể mới theo cách thủ công. persist()sẽ không phàn nàn rằng nó có ID, nó chỉ phàn nàn khi có một cái gì đó có cùng ID trong cơ sở dữ liệu.
giờ

1

Bạn có thể đã đến đây để được tư vấn về thời điểm sử dụng liên tục và khi nào nên sử dụng hợp nhất . Tôi nghĩ rằng nó phụ thuộc vào tình huống: khả năng bạn cần tạo một bản ghi mới và mức độ khó để lấy dữ liệu bền bỉ.

Giả sử bạn có thể sử dụng khóa / mã định danh tự nhiên.

  • Dữ liệu cần phải được duy trì, nhưng thỉnh thoảng một bản ghi tồn tại và một bản cập nhật được yêu cầu. Trong trường hợp này, bạn có thể thử kiên trì và nếu nó ném EntityExistsException, bạn tìm kiếm nó và kết hợp dữ liệu:

    thử {entityManager.persist (entity)}

    bắt (ngoại lệ EntityExistsException) {/ * truy xuất và hợp nhất * /}

  • Dữ liệu liên tục cần được cập nhật, nhưng thỉnh thoảng vẫn chưa có bản ghi nào cho dữ liệu. Trong trường hợp này, bạn tìm kiếm nó và tiếp tục thực hiện nếu thực thể bị thiếu:

    entity = entityManager.find (key);

    if (entity == null) {entityManager.persist (entity); }

    khác {/ * hợp nhất * /}

Nếu bạn không có khóa / mã định danh tự nhiên, bạn sẽ khó khăn hơn để tìm hiểu xem thực thể đó có tồn tại hay không, hoặc làm thế nào để tìm kiếm nó.

Việc sáp nhập cũng có thể được xử lý theo hai cách:

  1. Nếu các thay đổi thường nhỏ, áp dụng chúng cho thực thể được quản lý.
  2. Nếu các thay đổi là phổ biến, hãy sao chép ID từ thực thể bền vững, cũng như dữ liệu chưa được lọc. Sau đó gọi EntityManager :: merge () để thay thế nội dung cũ.

0

kiên trì (thực thể) nên được sử dụng với các thực thể hoàn toàn mới, để thêm chúng vào DB (nếu thực thể đã tồn tại trong DB thì sẽ có EntityExistsException throw).

hợp nhất (thực thể) nên được sử dụng, để đưa thực thể trở lại bối cảnh bền vững nếu thực thể bị tách ra và được thay đổi.

Có lẽ vẫn tồn tại đang tạo ra câu lệnh INSERT sql và hợp nhất câu lệnh UPDATE sql (nhưng tôi không chắc chắn).


Điều này là không chính xác. Nếu bạn gọi merge (e) trên một e mới, nó phải được duy trì.
Pedro Lamarão


Từ phiên bản đặc tả JPA 2.1, mục 3.2.7.1, dấu đầu dòng thứ hai: "Nếu X là một thực thể mới, một thực thể quản lý mới X 'được tạo và trạng thái của X được sao chép vào đối tượng thực thể được quản lý mới X'."
Pedro Lamarão 8/12/2015
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.