Lỗi Hibernate: một đối tượng khác có cùng giá trị số nhận dạng đã được liên kết với phiên


90

Về cơ bản, tôi có một số đối tượng trong cấu hình này (mô hình dữ liệu thực phức tạp hơn một chút):

  • A có mối quan hệ nhiều-nhiều với B. (B có inverse="true")
  • B có mối quan hệ nhiều-một với C. (Tôi đã cascadeđặt là "save-update")
  • C là một loại bảng loại / loại.

Ngoài ra, tôi có lẽ nên đề cập rằng các khóa chính được tạo bởi cơ sở dữ liệu khi lưu.

Với dữ liệu của mình, đôi khi tôi gặp sự cố trong đó A có một tập hợp các đối tượng B khác nhau và các đối tượng B này tham chiếu đến cùng một đối tượng C.

Khi tôi gọi session.saveOrUpdate(myAObject), tôi nhận được một lỗi ngủ đông nói: "a different object with the same identifier value was already associated with the session: C". Tôi biết rằng hibernate không thể chèn / cập nhật / xóa cùng một đối tượng hai lần trong cùng một phiên, nhưng có một số cách giải quyết vấn đề này? Tình huống này có vẻ không phải là hiếm.

Trong quá trình nghiên cứu vấn đề này, tôi đã thấy mọi người đề xuất sử dụng session.merge(), nhưng khi tôi làm điều đó, bất kỳ đối tượng "xung đột" nào sẽ được chèn vào cơ sở dữ liệu dưới dạng các đối tượng trống với tất cả các giá trị được đặt thành null. Rõ ràng đó không phải là điều chúng tôi muốn.

[Chỉnh sửa] Một điều khác mà tôi quên đề cập là (vì lý do kiến ​​trúc ngoài tầm kiểm soát của tôi), mỗi lần đọc hoặc ghi cần được thực hiện trong một phiên riêng biệt.


Xem nếu điều này câu trả lời giúp bạn ..
joaonlima

Câu trả lời:


96

Hầu hết có lẽ là do các đối tượng B không tham chiếu đến cùng một cá thể đối tượng Java C. Chúng đề cập đến cùng một hàng trong cơ sở dữ liệu (tức là cùng một khóa chính) nhưng chúng là các bản sao khác nhau của nó.

Vì vậy, những gì đang xảy ra là phiên Hibernate, đang quản lý các thực thể sẽ theo dõi đối tượng Java nào tương ứng với hàng có cùng khóa chính.

Một tùy chọn sẽ là đảm bảo rằng các Thực thể của đối tượng B tham chiếu đến cùng một hàng đang thực sự tham chiếu đến cùng một thể hiện đối tượng của C. Hoặc tắt xếp tầng cho biến thành viên đó. Theo cách này khi B vẫn còn C thì không. Tuy nhiên, bạn sẽ phải lưu C theo cách thủ công. Nếu C là một bảng loại / danh mục, thì nó có thể hợp lý theo cách đó.


3
Cảm ơn jbx. Như bạn đã nói, hóa ra các đối tượng B đang tham chiếu đến nhiều phiên bản C trong bộ nhớ. Về cơ bản những gì đang xảy ra là một phần của chương trình của tôi đang đọc bằng C và gắn nó với B. Phần khác đang tải một B khác với cùng một C từ cơ sở dữ liệu. Cả hai đều được gắn với A, điều này gây ra lỗi khi lưu. Tôi đã đặt <pre> cascade </pre> cho mối quan hệ B-> C thành "<pre> none </pre>" nhưng tôi vẫn gặp lỗi tương tự. Trong các mối quan hệ nhiều-một hoặc một-nhiều, có cách nào để nói Hibernate chỉ thay đổi khóa ngoại và không lo lắng về phần còn lại không?
John

1
Khóa chính của C có bất kỳ chiến lược tạo ID nào không? Giống như một trình tạo trình tự hoặc một cái gì đó tương tự?
jbx

Có, mỗi thứ có trình tự riêng trong cơ sở dữ liệu. Như bạn đã đề cập, xếp tầng hóa ra là một vấn đề. Chúng tôi đã tắt phân tầng cho các bảng loại và đối với những bảng khác, chúng tôi sử dụng phân tầng "hợp nhất", cho phép chúng tôi gọi merge () mà không cần tạo tất cả các hàng rỗng đó. Tôi đã đánh dấu câu trả lời của bạn cho phù hợp, cảm ơn!
John

14
Tôi đã sử dụng merge () thay vì saveOrUpdate () và BOOM! nó hoạt động :)
Lahiru Ruhunage

27

Chỉ cần đặt thác thành MERGE, điều đó sẽ thực hiện thủ thuật.


13

Bạn chỉ cần làm một việc. Chạy session_object.clear()và sau đó lưu đối tượng mới. Thao tác này sẽ xóa phiên (như được đặt tên phù hợp) và xóa đối tượng trùng lặp vi phạm khỏi phiên của bạn.


9

Tôi đồng ý với @Hemant Kumar, cảm ơn bạn rất nhiều. Theo giải pháp của anh ấy, tôi đã giải quyết được vấn đề của mình.

Ví dụ:

@Test
public void testSavePerson() {
    try (Session session = sessionFactory.openSession()) {
        Transaction tx = session.beginTransaction();
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("222");
        person2.setName("111");
        session.save(person1);
        session.save(person2);
        tx.commit();
    }
}

Person.java

public class Person {
    private int id;
    private String name;

    @Id
    @Column(name = "id")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Mã này luôn gây ra lỗi trong ứng dụng của tôi:, A different object with the same identifier value was already associated with the sessionsau này tôi phát hiện ra rằng tôi đã quên tự động tăng khóa chính của mình!

Giải pháp của tôi là thêm mã này vào khóa chính của bạn:

@GeneratedValue(strategy = GenerationType.AUTO)

5

Chuyển nhiệm vụ gán ID đối tượng từ Hibernate sang cơ sở dữ liệu bằng cách sử dụng:

<generator class="native"/>

Điều này giải quyết các vấn đề đối với tôi.


5

Điều này có nghĩa là bạn đang cố gắng lưu nhiều hàng trong bảng của mình với tham chiếu đến cùng một đối tượng.

kiểm tra thuộc tính id Lớp thực thể của bạn.

@Id
private Integer id;

đến

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;

1
không phải như vậy với câu hỏi theo mô tả được
Sudip Bhandari


3

Một cách để giải quyết vấn đề trên sẽ là ghi đè hashcode().
Đồng thời xóa phiên ngủ đông trước và sau khi lưu.

getHibernateTemplate().flush();

Đặt đối tượng tách rời một cách rõ ràng nullcũng hữu ích.


2

Chỉ cần xem thông báo này nhưng trong mã c #. Không chắc liệu nó có liên quan hay không (mặc dù chính xác là cùng một thông báo lỗi).

Tôi đang gỡ lỗi mã bằng các điểm ngắt và mở rộng một số bộ sưu tập thông qua các thành viên riêng tư trong khi trình gỡ lỗi ở điểm ngắt. Việc chạy lại mã mà không tìm hiểu cấu trúc đã làm cho thông báo lỗi biến mất. Có vẻ như hành động xem xét các bộ sưu tập được tải lười biếng riêng tư đã làm cho NHibernate tải những thứ không được tải vào thời điểm đó (vì chúng ở trong thành viên riêng tư).

Bản thân mã được bao bọc trong một giao dịch khá phức tạp có thể cập nhật số lượng lớn các bản ghi và nhiều phụ thuộc như một phần của giao dịch đó (quy trình nhập).

Hy vọng là một manh mối cho bất kỳ ai khác gặp phải vấn đề này.


2

Tìm thuộc tính "Cascade" trong Hibernate và xóa nó. Khi bạn đặt "Cascade" khả dụng, nó sẽ gọi các hoạt động khác (lưu, cập nhật và xóa) trên một thực thể khác có mối quan hệ với các lớp liên quan. Vì vậy, giá trị đồng nhất sẽ xảy ra. Nó đã làm việc với tôi.


1

Tôi đã gặp lỗi này vài ngày liên tục và tôi đã tăng quá nhiều thời gian để sửa lỗi này.

 public boolean save(OrderHeader header) {
    Session session = sessionFactory.openSession();


    Transaction transaction = session.beginTransaction();

    try {
        session.save(header);

        for (OrderDetail detail : header.getDetails()) {
            session.save(detail);
        }

        transaction.commit();
        session.close();

        return true;
    } catch (HibernateException exception) {

        exception.printStackTrace();
        transaction.rollback();
        return false;
    }
}

Trước khi gặp lỗi này, tôi đã không đề cập đến kiểu tạo ID trên Đối tượng OrderDetil. khi không tạo id Orderdetails, nó giữ Id là 0 cho mọi đối tượng OrderDetail. điều này #jbx đã giải thích. Vâng, đó là câu trả lời tốt nhất. một ví dụ này làm thế nào nó xảy ra.


1

Cố gắng đặt mã truy vấn của bạn trước đó. Điều đó khắc phục sự cố của tôi. ví dụ: thay đổi điều này:

query1 
query2 - get the error 
update

đến điều này:

query2
query1
update

0

bạn có thể không đặt mã định danh của đối tượng trước khi gọi truy vấn cập nhật.


3
Nếu không phải anh ấy thì anh ấy sẽ không gặp vấn đề này. Vấn đề là anh ta có hai đối tượng với cùng một định danh.
aalku

0

Tôi đã gặp sự cố do quá trình tạo khóa chính bị sai, khi tôi chèn một hàng như thế này:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) {
        // TODO Auto-generated method stub
        try {
            Set<Byte> keySet = map.keySet();
            for (Byte byte1 : keySet) {
                Device device=new Device();
                device.setNumDevice(DeviceCount.map.get(byte1));
                device.setTimestamp(System.currentTimeMillis());
                device.setTypeDevice(byte1);
                this.getHibernateTemplate().save(device);
            }
            System.out.println("hah");
        }catch (Exception e) {
            // TODO: handle exception
            logger.warn("wrong");
            logger.warn(e.getStackTrace()+e.getMessage());
        }
}

Tôi thay đổi lớp trình tạo id thành danh tính

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>

0

Trong trường hợp của tôi, chỉ có flush () không hoạt động. Tôi đã phải sử dụng clear () sau khi flush ().

public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}


0

Nếu để một tab biểu thức trong IDE của tôi đang mở đang thực hiện lệnh gọi nhận chế độ ngủ đông trên đối tượng gây ra ngoại lệ này. Tôi đã cố gắng xóa cùng một đối tượng này. Ngoài ra, tôi đã có một điểm ngắt trên cuộc gọi xóa mà dường như là cần thiết để lỗi này xảy ra. Chỉ cần tạo một tab biểu thức khác làm tab phía trước hoặc thay đổi cài đặt để lý tưởng không dừng lại trên các điểm ngắt đã giải quyết được vấn đề này.


0

Đảm bảo rằng đối tượng của bạn có cùng Loại tạo với tất cả các đối tượng được ánh xạ

Ví dụ: UserRole

public class UserRole extends AbstractDomain {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

@Enumerated(EnumType.STRING)
private CommonStatus status;

private String roleCode;

private Long level;

@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;

private String modification;

@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}

Mô-đun:

public class Modules implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

}

Thực thể chính với ánh xạ

public class RoleModules implements Serializable{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;

@Type(type = "yes_no")
private boolean isPrimaryModule;

public boolean getIsPrimaryModule() {
    return isPrimaryModule;
}

}


0

Ngoài tất cả các câu trả lời trước đó, có thể khắc phục sự cố này trong một dự án quy mô lớn, nếu bạn sử dụng Đối tượng giá trị cho các lớp của mình không đặt thuộc tính id trong lớp VO Transformer.


0

chỉ cam kết giao dịch hiện tại.

currentSession.getTransaction().commit();

bây giờ bạn có thể bắt đầu một Giao dịch khác và làm bất cứ điều gì trên thực thể

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.