Lỗi Hibernate: org.hibernate.NonUniqueObjectException: một đối tượng khác có cùng giá trị định danh đã được liên kết với phiên


114

Tôi có hai Đối tượng người dùng và trong khi tôi cố gắng lưu đối tượng bằng cách sử dụng

session.save(userObj);

Tôi nhận được lỗi sau:

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
[com.pojo.rtrequests.User#com.pojo.rtrequests.User@d079b40b]

Tôi đang tạo phiên bằng cách sử dụng

BaseHibernateDAO dao = new BaseHibernateDAO();          

rtsession = dao.getSession(userData.getRegion(),
                           BaseHibernateDAO.RTREQUESTS_DATABASE_NAME);

rttrans = rtsession.beginTransaction();
rttrans.begin();

rtsession.save(userObj1);
rtsession.save(userObj2);

rtsession.flush();
rttrans.commit();

rtsession.close(); // in finally block

Tôi cũng đã thử làm cách session.clear()cứu trước đó, vẫn không may mắn.

Đây là lần đầu tiên tôi nhận được đối tượng phiên khi một yêu cầu của người dùng đến, vì vậy tôi đang hiểu tại sao lại nói rằng đối tượng đó hiện diện trong phiên.

Bất kỳ đề xuất?


Đây là một chuỗi tuyệt vời khác đã giúp giải quyết sự cố của tôi getj2ee.over-blog.com/…
Reddymails

Câu trả lời:


173

Tôi đã gặp lỗi này nhiều lần và có thể khá khó để theo dõi ...

Về cơ bản, hibernate đang nói rằng bạn có hai đối tượng có cùng mã định danh (cùng khóa chính) nhưng chúng không phải là cùng một đối tượng.

Tôi sẽ đề nghị bạn chia nhỏ mã của mình, tức là nhận xét từng bit cho đến khi lỗi biến mất và sau đó đặt mã trở lại cho đến khi nó trở lại và bạn sẽ tìm ra lỗi.

Nó thường xảy ra nhất thông qua lưu theo tầng trong đó có lưu theo tầng giữa đối tượng A và B, nhưng đối tượng B đã được liên kết với phiên nhưng không nằm trên cùng một phiên bản của B với đối tượng trên A.

Bạn đang sử dụng trình tạo khóa chính nào?

Lý do tôi hỏi là lỗi này liên quan đến cách bạn đang nói với hibernate để xác định trạng thái liên tục của một đối tượng (tức là liệu một đối tượng có liên tục hay không). Lỗi có thể xảy ra vì chế độ ngủ đông đang cố gắng duy trì một đối tượng đã tồn tại lâu dài. Trên thực tế, nếu bạn sử dụng save hibernate sẽ cố gắng duy trì đối tượng đó và có thể đã có một đối tượng có cùng khóa chính được liên kết với phiên.

Thí dụ

Giả sử bạn có một đối tượng lớp ngủ đông cho một bảng có 10 hàng dựa trên tổ hợp khóa chính (cột 1 và cột 2). Bây giờ, bạn đã xóa 5 hàng khỏi bảng tại một thời điểm nào đó. Bây giờ, nếu bạn cố gắng thêm lại 10 hàng giống nhau, trong khi ngủ đông cố gắng duy trì các đối tượng trong cơ sở dữ liệu, 5 hàng đã bị xóa sẽ được thêm vào mà không có lỗi. Bây giờ 5 hàng còn lại đã tồn tại, sẽ loại bỏ ngoại lệ này.

Vì vậy, cách tiếp cận dễ dàng sẽ là kiểm tra xem bạn đã cập nhật / loại bỏ bất kỳ giá trị nào trong bảng là một phần của thứ gì đó và sau đó bạn có đang cố gắng chèn lại các đối tượng giống nhau không


4
Câu trả lời hay². Khóa chính là vấn đề của tôi, được giải quyết bằng GeneratedValue thiết lập trình tự cho postgresql.
Rodrigo Ferrari

2
Tôi gặp vấn đề tương tự. Trong trường hợp của tôi, tôi đã tìm kiếm một đối tượng trong một đoạn mã và tôi đang cố gắng tạo một đối tượng mới với cùng một ID trong đoạn mã khác trong khi đối tượng đầu tiên vẫn đang ở phiên ngủ đông.
dellasavia

18

Đây chỉ là một điểm mà chế độ ngủ đông gây ra nhiều vấn đề hơn là nó giải quyết được. Trong trường hợp của tôi, có nhiều đối tượng có cùng số nhận dạng 0, bởi vì chúng là mới và không có. Db tạo ra chúng. Ở đâu đó tôi đã đọc rằng 0 tín hiệu Id không được đặt. Cách trực quan để duy trì chúng là lặp lại chúng và nói ngủ đông để lưu các đối tượng. Nhưng Bạn không thể làm điều đó - "Tất nhiên Bạn nên biết rằng chế độ ngủ đông hoạt động theo cách này và cách kia, do đó Bạn phải .." Vì vậy, bây giờ tôi có thể thử thay đổi Id thành Long thay vì dài và xem nó có hoạt động không. Cuối cùng, việc thực hiện điều đó với một trình ánh xạ đơn giản của riêng bạn sẽ dễ dàng hơn, bởi vì ngủ đông chỉ là một gánh nặng bổ sung trong suốt. Một ví dụ khác: Việc cố gắng đọc các tham số từ một cơ sở dữ liệu và duy trì chúng trong một cơ sở dữ liệu khác buộc bạn phải thực hiện gần như tất cả các công việc theo cách thủ công.


13
Tôi cũng ghét ngủ đông ... và cơ sở dữ liệu ... Sau tất cả những vấn đề mà chúng gây ra cho tôi, tôi đoán việc sử dụng tệp văn bản sẽ dễ dàng hơn (tôi nói đùa, nhưng vẫn ...).
Igor Popov

Trong trường hợp của tôi, có nhiều đối tượng có cùng số nhận dạng 0, bởi vì chúng là mới và không có. Db tạo ra chúng. Ở đâu đó tôi đã đọc rằng 0 tín hiệu Id không được đặt. Cách trực quan để duy trì chúng là lặp lại chúng và nói ngủ đông để lưu các đối tượng . Tôi cần phải làm chính xác điều này. Bạn có thể cho tôi biết đó là "cách ngủ đông" để làm điều này?
Ramses

Trong trường hợp của tôi, tôi đã phải sử dụng session.merge (myobject) vì tôi có hai trường hợp của đối tượng này. Điều đó thường xảy ra khi một thực thể được duy trì và tách khỏi phiên. Một phiên bản khác của thực thể này được yêu cầu ở chế độ ngủ đông. Phiên bản thứ hai này vẫn gắn liền với phiên. Phiên bản đầu tiên được sửa đổi. Xem thêm tại getj2ee.over-blog.com/…
Reddymails

nó không phải là vấn đề tạo ra ngủ đông, mà là thiếu hiểu cách hoạt động của nó
ACV

17

USe session.evict(object);Chức năng của evict()phương thức được sử dụng để loại bỏ cá thể khỏi bộ đệm phiên. Vì vậy, lần đầu tiên lưu đối tượng, hãy lưu đối tượng bằng session.save(object)phương thức gọi trước khi loại bỏ đối tượng khỏi bộ đệm. Theo cách tương tự, hãy cập nhật đối tượng bằng cách gọi session.saveOrUpdate(object)hoặc session.update(object)trước khi gọi evict ().


11

Điều này có thể xảy ra khi bạn đã sử dụng cùng một đối tượng phiên để đọc và ghi. Làm sao? Giả sử bạn đã tạo một phiên. Bạn đọc một bản ghi từ bảng nhân viên với khóa chính Emp_id = 101 Bây giờ Bạn đã sửa đổi bản ghi trong Java. Và bạn sẽ lưu bản ghi Nhân viên trong cơ sở dữ liệu. chúng tôi đã không đóng phiên bất cứ nơi nào ở đây. Vì đối tượng được đọc cũng tồn tại trong phiên. Nó xung đột với đối tượng mà chúng ta muốn viết. Do đó lỗi này xảy ra.


9

Như ai đó đã chỉ ra ở trên, tôi đã gặp phải vấn đề này khi tôi cascade=allở cả hai đầu one-to-manymối quan hệ, vì vậy hãy giả sử A -> B (một-nhiều từ A và nhiều-một từ B) và đang cập nhật phiên bản của B trong A và sau đó gọi saveOrUpdate (A), nó dẫn đến một yêu cầu lưu vòng tròn, tức là lưu của A kích hoạt lưu của B kích hoạt lưu của A ... và trong trường hợp thứ ba khi thực thể (của A) đã được cố gắng được thêm vào sessionPersistenceContext ngoại lệ trùng lặp đối tượng đã được ném.
  Tôi có thể giải quyết nó bằng cách loại bỏ thác từ một đầu.


Giải quyết vấn đề của tôi cùng với stackoverflow.com/questions/4334970/...
Magno C

5

Bạn có thể sử dụng session.merge(obj), nếu bạn đang lưu với các phiên khác nhau với cùng một đối tượng liên tục số nhận dạng.
Nó hoạt động, tôi đã gặp vấn đề tương tự trước đây.


4

Tôi cũng gặp sự cố này và rất khó tìm ra lỗi.

Vấn đề tôi gặp phải là:

Vật thể đã bị một người Dao đọc phiên ngủ đông khác.

Để tránh ngoại lệ này, chỉ cần đọc lại đối tượng bằng dao sẽ lưu / cập nhật đối tượng này sau này.

vì thế:

class A{      

 readFoo(){
       someDaoA.read(myBadAssObject); //Different Session than in class B
    }

}

class B{



 saveFoo(){
       someDaoB.read(myBadAssObjectAgain); //Different Session than in class A
       [...]
       myBadAssObjectAgain.fooValue = 'bar';
       persist();
    }

}

Hy vọng rằng tiết kiệm một số người rất nhiều thời gian!


4

Tôi gặp sự cố này do:

  1. Xóa một đối tượng (sử dụng HQL)
  2. Lưu trữ ngay lập tức một đối tượng mới với cùng một id

Tôi đã giải quyết nó bằng cách xóa kết quả sau khi xóa và xóa bộ nhớ cache trước khi lưu đối tượng mới

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();

4

Sự cố này xảy ra khi chúng tôi cập nhật cùng một đối tượng của phiên, mà chúng tôi đã sử dụng để tìm nạp đối tượng từ cơ sở dữ liệu.

Bạn có thể sử dụng phương pháp hợp nhất của ngủ đông thay vì phương pháp cập nhật.

ví dụ: Đầu tiên sử dụng session.get () và sau đó bạn có thể sử dụng session.merge (đối tượng). Phương pháp này sẽ không tạo ra bất kỳ vấn đề nào. Chúng ta cũng có thể sử dụng phương thức merge () để cập nhật đối tượng trong cơ sở dữ liệu.


3

Lấy đối tượng bên trong phiên, đây là một ví dụ:

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);

3
Rất vui, nhưng đối tượng "ob" được trả về mà không có dữ liệu cập nhật (xem xét rằng nó đã được cập nhật thông qua ứng dụng trong thời gian chạy, giữa các đối tượng truy xuất từ ​​cơ sở dữ liệu và lưu nó).
Alex

2

Các ánh xạ Id của bạn có chính xác không? Nếu cơ sở dữ liệu chịu trách nhiệm tạo Id thông qua số nhận dạng, bạn cần ánh xạ đối tượng sử dụng của mình tới đó ..


2

Tôi gặp phải sự cố này với việc xóa một đối tượng, không trục xuất cũng không xóa đã giúp.

/**
 * Deletes the given entity, even if hibernate has an old reference to it.
 * If the entity has already disappeared due to a db cascade then noop.
 */
public void delete(final Object entity) {
  Object merged = null;
  try {
    merged = getSession().merge(entity);
  }
  catch (ObjectNotFoundException e) {
    // disappeared already due to cascade
    return;
  }
  getSession().delete(merged);
}

2

trước vị trí mà các đối tượng lặp lại bắt đầu, bạn nên đóng phiên và sau đó bạn nên bắt đầu một phiên mới

session.close();      
session = HibernateUtil.getSessionFactory().openSession();

vì vậy theo cách này trong một phiên không có nhiều hơn một thực thể có cùng số nhận dạng.


2

Đến bữa tiệc muộn, nhưng có thể giúp ích cho những người dùng sắp tới -

Tôi gặp sự cố này khi tôi chọn bản ghi bằng cách sử dụng getsession()cập nhật lại bản ghi khác có cùng số nhận dạng bằng cách sử dụng cùng một phiên gây ra sự cố. Đã thêm mã bên dưới.

Customer existingCustomer=getSession().get(Customer.class,1);
Customer customerFromUi;// This customer details comiong from UI with identifer 1

getSession().update(customerFromUi);// Here the issue comes

Điều này không bao giờ nên được thực hiện. Giải pháp là loại bỏ phiên trước khi cập nhật hoặc thay đổi logic nghiệp vụ.


1

Kiểm tra xem bạn có quên đặt @GenerateValue cho cột @Id hay không. Tôi đã có cùng một vấn đề với nhiều mối quan hệ giữa Phim và Thể loại. Chương trình đã tạo ra Lỗi Hibernate: org.hibernate.NonUniqueObjectException: một đối tượng khác có cùng giá trị định danh đã được liên kết với lỗi phiên. Sau đó, tôi phát hiện ra rằng tôi chỉ cần đảm bảo rằng bạn có @GenerateValue cho phương thức GenreId get.


làm thế nào tôi có thể áp dụng giải pháp của bạn cho câu hỏi của tôi? tôi có tạo cột id trong lớp Tác vụ không? stackoverflow.com/questions/55732955/…
sbattou

1

chỉ cần kiểm tra id xem nó nhận null hay 0 như thế nào

if(offersubformtwo.getId()!=null && offersubformtwo.getId()!=0)

thêm hoặc cập nhật nơi nội dung được đặt từ biểu mẫu thành Pojo


1

Tôi mới sử dụng NHibernate và vấn đề của tôi là tôi đã sử dụng một phiên khác để truy vấn đối tượng của mình hơn là tôi đã làm để lưu nó. Vì vậy, phiên lưu không biết về đối tượng.

Nó có vẻ hiển nhiên, nhưng từ việc đọc các câu trả lời trước, tôi đã tìm kiếm khắp nơi cho 2 đối tượng, không phải 2 phiên.


1

@GeneratedValue (chiến lược = GenerationType.IDENTITY), thêm chú thích này vào thuộc tính khóa chính trong đậu thực thể của bạn sẽ giải quyết được vấn đề này.


1

Tôi đã giải quyết vấn đề này.
Trên thực tế, điều này đang xảy ra vì chúng ta đã quên triển khai Thuộc tính PK của Generator Type trong lớp bean. Vì vậy, hãy làm cho nó bất kỳ kiểu nào như

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

khi chúng ta duy trì các đối tượng của bean, mọi đối tượng có được cùng một ID, vì vậy đối tượng đầu tiên được lưu, khi đối tượng khác được duy trì thì HIB FW thông qua loại Exception: org.hibernate.NonUniqueObjectException:đối tượng khác với cùng giá trị định danh đã được liên kết với phiên.


1

Sự cố xảy ra vì trong cùng một phiên ngủ đông, bạn đang cố gắng lưu hai đối tượng có cùng số nhận dạng. Có hai giải pháp: -

  1. Điều này xảy ra do bạn chưa định cấu hình tệp mapping.xml của mình một cách chính xác cho các trường id như bên dưới: -

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. Ghi đè phương thức getession để chấp nhận một Tham số như isSessionClear và xóa phiên trước khi trả về phiên hiện tại như bên dưới

    public static Session getSession(boolean isSessionClear) {
        if (session.isOpen() && isSessionClear) {
            session.clear();
            return session;
        } else if (session.isOpen()) {
            return session;
        } else {
            return sessionFactory.openSession();
        }
    }

Điều này sẽ làm cho các đối tượng phiên hiện có bị xóa và ngay cả khi chế độ ngủ đông không tạo ra một số nhận dạng duy nhất, giả sử bạn đã định cấu hình cơ sở dữ liệu của mình đúng cách cho khóa chính bằng cách sử dụng một cái gì đó như Auto_Increment, nó sẽ hoạt động cho bạn.


1

Tôi đã có một vấn đề tương tự. Trong trường hợp của tôi, tôi đã quên đặt increment_bygiá trị trong cơ sở dữ liệu giống như giá trị được sử dụng bởi cache_sizeallocationSize. (Các mũi tên trỏ đến các thuộc tính đã đề cập)

SQL:

CREATED         26.07.16
LAST_DDL_TIME   26.07.16
SEQUENCE_OWNER  MY
SEQUENCE_NAME   MY_ID_SEQ
MIN_VALUE       1
MAX_VALUE       9999999999999999999999999999
INCREMENT_BY    20 <-
CYCLE_FLAG      N
ORDER_FLAG      N
CACHE_SIZE      20 <-
LAST_NUMBER     180

Java:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)

1

Mặt khác, so với những gì wbdarby đã nói, nó thậm chí có thể xảy ra khi một đối tượng được tìm nạp bằng cách đưa mã định danh của đối tượng đó cho HQL. Trong trường hợp cố gắng sửa đổi các trường đối tượng và lưu nó trở lại DB (sửa đổi có thể chèn, xóa hoặc cập nhật) trong cùng một phiên , lỗi này sẽ xuất hiện. Hãy thử xóa phiên ngủ đông trước khi lưu đối tượng đã sửa đổi của bạn hoặc tạo một phiên hoàn toàn mới.

Hy vọng tôi đã giúp ;-)


1

Tôi cũng gặp lỗi tương tự khi thay Bộ của mình bằng Bộ mới từ Jackson.

Để giải quyết vấn đề này, tôi giữ tập hợp hiện có, tôi xóa khỏi tập hợp cũ phần tử chưa biết vào danh sách mới với retainAll. Sau đó, tôi thêm những cái mới với addAll.

    this.oldSet.retainAll(newSet);
    this.oldSet.addAll(newSet);

Không cần phải có Session và thao tác với nó.


1

Thử cái này. Dưới đây làm việc cho tôi!

Trong hbm.xmltập tin

  1. Chúng ta cần đặt dynamic-updatethuộc tính của thẻ lớp thành true:

    <class dynamic-update="true">
  2. Đặt thuộc tính lớp của thẻ trình tạo trong cột duy nhất thành identity:

    <generator class="identity">

Lưu ý: Đặt cột duy nhất thành identitythay vì assigned.


0

Một điều khác hiệu quả với tôi là làm cho biến cá thể Long thay cho giá trị dài


Tôi đã có id dài biến khóa chính của mình; thay đổi nó thành Long id; đã làm việc

Tất cả tốt nhất


0

Bạn luôn có thể thực hiện một phiên tuôn ra. Flush sẽ đồng bộ hóa trạng thái của tất cả các đối tượng của bạn trong phiên (làm ơn, ai đó sửa cho tôi nếu tôi sai) và có thể nó sẽ giải quyết được vấn đề của bạn trong một số trường hợp.

Việc triển khai mã bằng và mã băm của riêng bạn cũng có thể giúp ích cho bạn.


0

Bạn có thể kiểm tra Cài đặt xếp tầng của mình. Cài đặt Cascade trên các kiểu máy của bạn có thể gây ra điều này. Tôi đã xóa Cài đặt xếp tầng (Về cơ bản là không cho phép Chèn / Cập nhật xếp tầng) và điều này đã giải quyết được vấn đề của tôi


0

Tôi cũng tìm thấy lỗi này. Điều phù hợp với tôi là đảm bảo rằng khóa chính (được tạo tự động) không phải là PDT (ví dụ: long, int, vv.), Mà là một đối tượng (tức là Long, Integer, v.v.)

Khi bạn tạo đối tượng của mình để lưu nó, hãy đảm bảo bạn chuyển null chứ không phải 0.


0

Không giúp đỡ à?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 

0

Tôi đã giải quyết một vấn đề tương tự như vậy:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
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.