1

I have a model which has 2 oneToMany relation to another 2 model. When i remove 1 record from first bag and save the object, hibernate removes 1 record from 1st bag which is ok but it also removes the record which has same index as the record in first bag from 2nd bag. I can not figure it out how to fix it.

Here is my models

Owner:

@Entity
@Table(name = "table_a")
public class Owner extends Serializable {

 private static final long serialVersionUID = 1L;

 @Id
 @Column(name = "id", unique = true, nullable = false)
 private int id;

 @OneToMany(mappedBy = "owner", fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.ALL)
 @OrderColumn(name = "position")
 private List<Dog> dogs;

 @OneToMany(mappedBy = "owner", fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.ALL)
 @OrderColumn(name = "position")
 private List<Cat> cats;

 public Long getId() {
    this.id;     
 }

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

 public Dog getDogs() {
    return this.dogs;
 }

 public void setDogs(List<Dog> dogs) {
    this.dogs = dogs;
 }

 public void addDog(Dog dog) {
    dog.owner = this;
    this.dogs.add(dog);
 }

 public void removeDog(Dog dog) {
    this.dogs.remove(dog);
 }

  public Dog getCats() {
    return this.cats;
 }

 public void setCats(List<Cat> cats) {
    this.cats = cats;
 }

 public void addCat(Cat cat) {
    Cat.owner = this;
    this.cats.add(cat);
 }

 public void removeCat(Cat cat) {
    this.cats.remove(cat);
 }
}   

Dog:

@Entity
@Table(name = "table_b")
public class Dog extends Serializable {

 private static final long serialVersionUID = 1L;

 @Id
 @Column(name = "id", unique = true, nullable = false)
 private int id;

 @ManyToOne
 @JoinColumn(name = "owner_id")
 private Owner owner;

 @Column(name = "position")
 private int position;

 public Long getId() {
    return this.id;
 }

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

 public int getPosition() {
    return this.position;
 }

 public void setPosition(int index) {
    this.position = index;
 }

 public Owner getOwner() {
    return this.owner;
 }

 public void setOwner(Owner owner) {
    this.owner = owner;
 }
} 

Cat:

@Entity
@Table(name = "table_c")
public class Cat extends Serializable {

 private static final long serialVersionUID = 1L;

 @Id
 @Column(name = "id", unique = true, nullable = false)
 private int id;

 @ManyToOne
 @JoinColumn(name = "owner_id")
 private Owner owner;

 @Column(name = "position")
 private int position;

 public Long getId() {
    return this.id;
 }

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

 public int getPosition() {
    return this.position;
 }

 public void setPosition(int index) {
    this.position = index;
 }

 public Owner getOwner() {
    return this.owner;
 }

 public void setOwner(Owner owner) {
    this.owner = owner;
 }
}   

Let say we had an owner which has 2 cats and 2 dogs.

when i do that:

Cat cat2Remove = owner.getCats().get(1);
owner.removeCat(cat2Remove);
session.save(owner);

Hibernate removes 2nd cat from table_b as i intend but it also removes 2nd dog from table_c and i wonder how can i prevent this in a proper way?

Omer Temel
  • 804
  • 1
  • 7
  • 18
  • What do you mean with *also removes*? Is the record of dog also deleted from database or is it just retrieving/fetching the same amount of dogs that cats for the related collections? Please check this. I think you are suffering problems with fetching behavior due to `Owner` has two eager and indexed collection. – Guillermo Aug 20 '15 at 06:35
  • I've no problems with fetching. With @OrderColumn annotation hibernate handles eager fetching multiple bags. However, the record of dog also deleted from database. – Omer Temel Aug 20 '15 at 07:59
  • In that case try to do `cat2remove.serOwner(null)` instead of `owner.removeCat(cat2Remove)`. This will respect JPA specification clause that says we should care about setting the owner side of bidirectional association, maybe hibernate works better this way – Guillermo Aug 20 '15 at 08:07

3 Answers3

0

This happens due to cascade = CascadeType.ALL so you have to remove CascadeType.ALL to understand the effect of cascade see this link.

if you want only to cascade on save-update use cascade="save-update" i don't know if you need orphanRemoval = true in your case or not but look at these answers to see if you need it in your business or not.

Community
  • 1
  • 1
karim mohsen
  • 2,164
  • 1
  • 15
  • 19
0

You need to generate a proper equals and hashCode pair to both of your entities. Right now they have the exact same set of variables (id, owner, position) and likely being treated as "equals", hence they are being removed. If you write a proper equals check which also checks the object type (Eclipse can generate this for you under "Source" generate equals/hashcode menu) your problem should disappear instantly.

Gergely Bacso
  • 14,243
  • 2
  • 44
  • 64
0

As I mentioned in the comment, I think the underlying problem is how fetching strategy works for the case with two eager ordered collections. So to earn time I suggest you to try adding the follow Hibernate's annotation to both collection:

@Fetch(FetchMode.SELECT)

It tells to Hibernate that the collection's elements should be retrieved executing a separated select query from the one that retrieve the Owner instance information. It means without doing a join statement.

Guillermo
  • 1,523
  • 9
  • 19