Hibernate - Một bộ sưu tập với cascade = tất cả xóa-mồ côi 'không còn được tham chiếu bởi thể hiện thực thể sở hữu


225

Tôi gặp vấn đề sau khi cố gắng cập nhật thực thể của mình:

"A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance".

Tôi có một thực thể cha mẹ và nó có một Set<...>số thực thể con. Khi tôi cố gắng cập nhật nó, tôi nhận được tất cả các tham chiếu được đặt vào bộ sưu tập này và đặt nó.

Đoạn mã sau thể hiện ánh xạ của tôi:

@OneToMany(mappedBy = "parentEntity", fetch = FetchType.EAGER)
@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
public Set<ChildEntity> getChildren() {
    return this.children;
}

Tôi đã cố gắng chỉ làm sạch Bộ <..>, theo cách này: Cách "có thể" giải quyết vấn đề nhưng nó không hoạt động.

Nếu bạn có ý kiến ​​khác, hãy nói cho tôi biết.

Cảm ơn!


1
@ mel3kings, liên kết bạn cung cấp không còn hoạt động.
Opal


thử sử dụng các bộ sưu tập có thể thay đổi khi loại bỏ các yếu tố. Ví dụ: không sử dụng something.manyother.remove(other)nếu manyotherlà a List<T>. Tạo nhiều tính năng có thể thay đổi, thích ArrayList<T>và sử dụngorphanDelete = true
Nurettin

Câu trả lời:


220

Kiểm tra tất cả những nơi mà bạn đang gán một cái gì đó cho sonEntities. Liên kết bạn đã tham chiếu chỉ ra rõ ràng việc tạo Hashset mới nhưng bạn có thể gặp lỗi này bất cứ khi nào bạn gán lại bộ. Ví dụ:

public void setChildren(Set<SonEntity> aSet)
{
    this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
}

Thông thường bạn chỉ muốn "mới" bộ một lần trong hàm tạo. Bất cứ khi nào bạn muốn thêm hoặc xóa một cái gì đó vào danh sách, bạn phải sửa đổi nội dung của danh sách thay vì chỉ định một danh sách mới.

Để thêm con:

public void addChild(SonEntity aSon)
{
    this.sonEntities.add(aSon);
}

Để loại bỏ trẻ em:

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}

8
Trên thực tế, vấn đề của tôi là về bằng và mã băm của các thực thể của tôi. Một mã kế thừa có thể mang lại rất nhiều vấn đề, đừng bao giờ quên kiểm tra nó. Tất cả những gì tôi đã làm chỉ là giữ chiến lược xóa mồ côi và sửa mã bằng và mã băm.
axcdnt

6
Tôi rất vui vì bạn đã giải quyết vấn đề của bạn. bằng và mã băm đã cắn tôi vài lần với Hibernate. Thay vì cập nhật tiêu đề của câu hỏi bằng "[Đã giải quyết]", bạn nên tiếp tục và đăng câu trả lời của mình và sau đó đánh dấu nó là câu trả lời được chấp nhận.
brainimus

Cảm ơn, tôi đã chạy vào một cái gì đó tương tự và hóa ra setter của tôi cho Set đó trống rỗng ..
Siddhartha

có ngay cả trong danh sách trống, bạn nhận được lỗi này. tôi không mong đợi điều đó bởi vì không có trẻ mồ côi (danh sách trống rỗng).
tibi

4
Thông thường bạn chỉ muốn "mới" bộ một lần trong hàm tạo. Bất cứ khi nào bạn muốn thêm hoặc xóa một cái gì đó vào danh sách, bạn phải sửa đổi nội dung của danh sách thay vì chỉ định một danh sách mới. Nhất imp
Nikhil Sahu

109

Phương pháp:

public void setChildren(Set<SonEntity> aSet) {
    this.sonEntities = aSet;
}

hoạt động nếu parentEntityđược tách ra và một lần nữa nếu chúng tôi cập nhật nó.
Nhưng nếu thực thể không tách rời khỏi mỗi bối cảnh, (tức là các hoạt động tìm và cập nhật nằm trong cùng một giao dịch) thì phương thức dưới đây hoạt động.

public void setChildren(Set<SonEntity> aSet) {
    //this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
    this.sonEntities.clear();
    if (aSet != null) {
        this.sonEntities.addAll(aSet);
    }
}

1
@Skuld Tôi có một vấn đề tương tự và tôi đã áp dụng giải pháp của bạn (trong phương thức setter tôi xóa bộ sưu tập trẻ em - this.children.clear () - và tôi đã thêm những đứa trẻ mới - this.children.add ALL (trẻ em)). Thay đổi này đã không khắc phục vấn đề của tôi. Tôi vẫn nhận được "Một bộ sưu tập với cascade =" all-xóa-mồ côi "không còn được tham chiếu bởi trường hợp thực thể sở hữu" ngoại lệ ". Bạn có một ý tưởng tại sao? Cảm ơn rât nhiều!
ovdsrn

@ovdsrn Xin lỗi, đây không phải là câu trả lời của tôi, tôi chỉ sắp xếp định dạng của câu trả lời, tác giả gốc (kmmanu) có thể giúp bạn (hoặc bạn có thể muốn bắt đầu một câu hỏi mới nếu kịch bản của bạn khác với câu hỏi ban đầu được hỏi ở đây) Chúc may mắn
Skuld

1
Điều này hoạt động tốt, nhưng không tốt lắm khi những đứa trẻ được chứa trong một thành phần ngủ đông lồng nhau và thành phần này được tạo thành null trong Thực thể của nó. Sau đó, bạn nhận được cùng một lỗi. Điều này là do chủ sở hữu của trẻ em là Thực thể gốc chứ không phải là thành phần được tạo thành null ... Như vậy, một thành phần không bao giờ được phép trở thành null, nhưng sẽ dẫn đến một phương thức hủy () trong trẻ con ... Ít nhất, tôi không biết một giải pháp tốt hơn ... Đó là một kiểu xây dựng bộ sưu tập rõ ràng () nhưng sau đó trên một thành phần thông qua phá hủy () ...
edbras

Xem thêm bài đăng này để biết thêm chi tiết ở trên: stackoverflow.com/questions/4770262/ trên
edbras

7
sử dụng this.sonEntities.retain ALL (a Set) trên sonEntities.clear () bởi vì nếu a set == this.sonEntities (tức là cùng một đối tượng), bạn sẽ xóa tập hợp trước khi thêm vào nó!
Martin

33

Khi tôi đọc ở nhiều nơi mà ngủ đông không thích bạn gán cho bộ sưu tập, tôi cho rằng điều an toàn nhất để làm rõ ràng là làm cho nó cuối cùng như thế này:

class User {
  private final Set<Role> roles = new HashSet<>();

public void setRoles(Set<Role> roles) {
  this.roles.retainAll(roles);
  this.roles.addAll(roles);
}
}

Tuy nhiên, điều này không hoạt động và bạn nhận được lỗi "không còn được tham chiếu" đáng sợ, điều này thực sự khá sai lệch trong trường hợp này.

Nó chỉ ra rằng ngủ đông gọi phương thức setRoles của bạn VÀ nó muốn lớp bộ sưu tập đặc biệt của nó được cài đặt ở đây và sẽ không chấp nhận lớp bộ sưu tập của bạn. Điều này đã khiến tôi bối rối trong một thời gian dài, mặc dù đã đọc tất cả các cảnh báo về việc không gán cho bộ sưu tập của bạn trong phương thức thiết lập của bạn.

Vì vậy, tôi đã thay đổi thành này:

public class User {
  private Set<Role> roles = null;

  public void setRoles(Set<Role> roles) {
  if (this.roles == null) {
    this.roles = roles;
  } else {
    this.roles.retainAll(roles);
   this.roles.addAll(roles);
  }
}
}

Vì vậy, trong cuộc gọi đầu tiên, hibernate cài đặt lớp đặc biệt của nó và trong các cuộc gọi tiếp theo, bạn có thể tự sử dụng phương thức mà không làm hỏng mọi thứ. Nếu bạn muốn sử dụng lớp của mình như một bean, có lẽ bạn cần một setter hoạt động, và điều này ít nhất có vẻ hiệu quả.


2
Tại sao bạn gọi giữ lại ALL ()? Tại sao không rõ ràng () theo sau bởi addAll ()?
edbras

1
"Tại sao bạn gọi lại ALL ALL ()? Tại sao không rõ ràng () theo sau bởi addAll ()?" Câu hỏi hay, tôi nghĩ rằng tôi đã làm việc theo giả định rằng ngủ đông sẽ ít có khả năng xem đây là cập nhật cơ sở dữ liệu nếu bạn không xóa các mục trước khi thêm lại chúng. Nhưng tôi nghi ngờ nó không hoạt động theo cách nào.
xpusostomos

2
Bạn nên sử dụng keepAll () trên clear () nếu không bạn có thể xóa sạch các vai trò nếu bạn tình cờ chuyển trong đối tượng được đặt giống hệt nhau. Ví dụ: user.setRoles (user.getRoles ()) == user.roles.clear ()
Martin

2
Khi bạn đặt orphanRemoval = true và bạn tạo ra một bản ghi trong đó các bộ sưu tập này là null, bạn cũng sẽ gặp lỗi này. Vậy: a có oneToMany b với orphanremoval = true. Khi bạn tạo A trong đó B = null, vấn đề này là kích hoạt. Giải pháp của bạn để khởi tạo và làm cho nó cuối cùng có vẻ tốt nhất.
Lawrence

Cảm ơn bạn! Tôi đã khởi tạo Danh sách của tôi là List<String> list = new ArrayList<>();. Thay đổi nó để List<String> list = null;khắc phục sự cố :)
Cấp tiến

19

Trên thực tế, vấn đề của tôi là về bằng và mã băm của các thực thể của tôi. Một mã kế thừa có thể mang lại rất nhiều vấn đề, đừng bao giờ quên kiểm tra nó. Tất cả những gì tôi đã làm chỉ là giữ chiến lược xóa mồ côi và sửa mã bằng và mã băm.


9
xin vui lòng, một ví dụ về một phương thức bằng và hashCode được thực hiện tốt? bởi vì tôi có rất nhiều vấn đề: hoặc tôi không thể cập nhật bộ của mình hoặc tôi gặp lỗi StackOverflow. tôi đã mở một câu hỏi ở đây: stackoverflow.com/questions/24737145/ . cảm ơn
SaganTheBest

11

Tôi đã có những lỗi giống nhau. Vấn đề đối với tôi là, sau khi lưu thực thể, bộ sưu tập được ánh xạ vẫn là null và khi cố gắng cập nhật thực thể, ngoại lệ đã bị ném. Điều gì đã giúp tôi: Lưu thực thể, sau đó tạo lại (bộ sưu tập không còn là null) và sau đó thực hiện cập nhật. Có thể khởi tạo bộ sưu tập với ArrayList () mới hoặc một cái gì đó cũng có thể giúp ích.


Gửi ArrayList mới thay vì null, làm việc cho tôi. Cảm ơn
rpajaziti

4

ĐÃ CÓ LOẠI LIÊN QUAN:


Đừng cố khởi tạo bộ sưu tập khi nó được khai báo hasMany, chỉ cần thêm và xóa các đối tượng.

class Parent {
    static hasMany = [childs:Child]
}

SỬ DỤNG LOẠI LIÊN QUAN:


Nhưng bộ sưu tập chỉ có thể là null khi được khai báo là thuộc tính (quan hệ sử dụng) và không được khởi tạo trong khai báo.

class Parent {
    List<Child> childs = []
}

4

Tôi đã sử dụng phương pháp @ user2709454 với sự cải thiện nhỏ.

public class User {
    private Set<Role> roles;

    public void setRoles(Set<Role> roles) {
        if (this.roles == null) {
            this.roles = roles;
        } else if(this.roles != roles) { // not the same instance, in other case we can get ConcurrentModificationException from hibernate AbstractPersistentCollection
            this.roles.clear();
            if(roles != null){
                this.roles.addAll(roles);
            }
        }
    }
}

3

Tôi đã có vấn đề này khi cố gắng sử dụng TreeSet. Tôi đã khởi tạo oneToManyvới TreeSetnhững gì hoạt động

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private Set<WizardAnswer> answers = new TreeSet<WizardAnswer>();

Nhưng, điều này sẽ mang lại lỗi được mô tả ở questiontrên. Vì vậy, có vẻ như hibernateđược hỗ trợ SortedSetvà nếu chỉ cần thay đổi dòng trên

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private SortedSet<WizardAnswer> answers;

nó hoạt động như ma thuật :) thông tin thêm về hibernate SortedSetcó thể ở đây


Có lẽ, tốt hơn là người dùng Collections.emptyset () sử dụng danh sách trống đơn
ryzhman

3

Lần duy nhất tôi gặp lỗi này là khi tôi cố gắng chuyển NULL vào setter cho bộ sưu tập. Để ngăn chặn điều này, setters của tôi trông như thế này:

public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
    if(submittedForms == null) {
        this.submittedForms.clear();
    }
    else {
        this.submittedForms = submittedForms;
    }
}

3

Tôi gặp phải vấn đề này khi cập nhật một thực thể với yêu cầu bài viết JSON. Lỗi xảy ra khi tôi cập nhật thực thể không có dữ liệu về trẻ em, ngay cả khi không có. Thêm

"children": [],

để cơ quan yêu cầu giải quyết vấn đề.


1

Một nguyên nhân khác có thể là sử dụng lombok.

@Builder- nguyên nhân để tiết kiệm Collections.emptyList()ngay cả khi bạn nói.myCollection(new ArrayList());

@Singular - bỏ qua các mặc định cấp lớp và rời khỏi trường null ngay cả khi trường lớp được khai báo làmyCollection = new ArrayList()

2 xu của tôi, chỉ mất 2 giờ với cùng :)


1

Tôi đã nhận được A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instancekhi tôi đang thiết lập parent.setChildren(new ArrayList<>()). Khi tôi đổi thànhparent.getChildren().clear() , nó đã giải quyết vấn đề.

Kiểm tra để biết thêm chi tiết: HibernateException - Một bộ sưu tập với cascade = "all-xóa-mồ côi" không còn được tham chiếu bởi thể hiện thực thể sở hữu .


1

Tôi đang sử dụng Spring Boot và gặp vấn đề này với một bộ sưu tập, mặc dù không trực tiếp ghi đè lên nó, bởi vì tôi đang khai báo một trường bổ sung cho cùng một bộ sưu tập với trình tuần tự hóa và trình giải tuần tự tùy chỉnh để cung cấp một đại diện thân thiện hơn với dữ liệu:

  public List<Attribute> getAttributes() {
    return attributes;
  }

  public void setAttributes(List<Attribute> attributes) {
    this.attributes = attributes;
  }

  @JsonSerialize(using = AttributeSerializer.class)
  public List<Attribute> getAttributesList() {
    return attributes;
  }

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes = attributes;
  }

Có vẻ như mặc dù bản thân tôi không ghi đè lên bộ sưu tập , nhưng quá trình khử lưu huỳnh thực hiện nó dưới mui xe, gây ra vấn đề này giống nhau. Giải pháp là thay đổi setter được liên kết với trình giải mã để nó xóa danh sách và thêm mọi thứ, thay vì ghi đè lên nó:

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);
  }

1

Tôi đã có cùng một vấn đề, nhưng đó là khi bộ này là null. Chỉ trong bộ sưu tập Set trong danh sách công việc tìm. Bạn có thể thử chú thích ngủ đông @LazyCollection (LazyCollectionOption.FALSE) bắt nguồn từ chú thích JPA fetch = FetchType.EAGER.

Giải pháp của tôi: Đây là cấu hình của tôi và hoạt động tốt

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private Set<Barcode> barcodes;

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private List<FormatAdditional> additionals;

1
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>();

Tôi đã gặp lỗi tương tự khi tôi thêm đối tượng con vào danh sách Đối tượng con hiện có.

childService.saveOrUpdate(child);
parent.addToChildren(child);
parentService.saveOrUpdate(parent);

Điều gì đã giải quyết vấn đề của tôi đang thay đổi thành:

child = childService.saveOrUpdate(child);

Bây giờ đứa trẻ đang hồi sinh với các chi tiết khác là tốt và nó hoạt động tốt.


0

Thêm câu trả lời ngu ngốc của tôi. Chúng tôi đang sử dụng Spring Data Rest. Đây là mối quan hệ khá chuẩn của chúng tôi. Các mô hình đã được sử dụng ở nơi khác.

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
List<Child> children = new LinkedList<>()


//Child class
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = 'ParentID', updatable = false)
@JsonBackReference
Parent parent

Với mối quan hệ chúng tôi tạo ra, luôn có ý định rằng những đứa trẻ sẽ được thêm vào thông qua repo của chính chúng. Tôi chưa thêm repo. Thử nghiệm tích hợp mà chúng tôi đã trải qua trong vòng đời hoàn chỉnh của thực thể thông qua các lệnh gọi REST để các giao dịch sẽ đóng giữa các yêu cầu. Không có repo cho đứa trẻ có nghĩa là json đã có những đứa trẻ như là một phần của cấu trúc chính thay vì trong _embedded. Cập nhật cho phụ huynh sau đó sẽ gây ra vấn đề.


0

Giải pháp sau đây làm việc cho tôi

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@OrderBy(value="ordinal ASC")
List<Child> children = new ArrayList<>()

//Updated setter of children 
public void setChildren(List<Children> children) {
    this.children.addAll(children);
    for (Children child: children)
        child.setParent(this);
}


//Child class
@ManyToOne
@JoinColumn(name="Parent_ID")
private Parent parent;

0

Thay vì chỉ định bộ sưu tập mới

public void setChildren(Set<ChildEntity> children) {
    this.children = children;
}

Thay thế tất cả các yếu tố bằng

public void setChildren(Set<ChildEntity> children) {
    Collections.replaceAll(this.children,children);
}

0

cẩn thận với

BeanUtils.copyProperties(newInsum, insumOld,"code");

Phương pháp này quá phá vỡ ngủ đông.



0

Của tôi hoàn toàn khác với Spring Boot! Đối với tôi nó không phải là do thiết lập bộ sưu tập tài sản.

Trong các thử nghiệm của mình, tôi đã cố gắng tạo một thực thể và nhận được lỗi này cho một bộ sưu tập khác không được sử dụng!

Sau rất nhiều cố gắng, tôi chỉ cần thêm một @Transactionalphương pháp thử nghiệm và nó đã giải quyết nó. Đừng có lý do mặc dù.


0

Điều này trái ngược với các câu trả lời trước đó, tôi có cùng một lỗi: "Một bộ sưu tập với cascade = Khăn all-xóa-mồ côi đã không còn được tham chiếu ...." khi hàm setter của tôi trông như thế này:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    if( this.taxCalculationRules == null ) {
        this.taxCalculationRules = taxCalculationRules_;
    } else {
        this.taxCalculationRules.retainAll(taxCalculationRules_);
        this.taxCalculationRules.addAll(taxCalculationRules_);
    }
}

Và rồi nó biến mất khi tôi đổi nó thành phiên bản đơn giản:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    this.taxCalculationRules = taxCalculationRules_;
}

(phiên bản ngủ đông - đã thử cả 5.4.10 và 4.3.11. Đã dành vài ngày để thử tất cả các loại giải pháp trước khi quay lại với nhiệm vụ đơn giản trong trình cài đặt.

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.