Reputation: 109
I have three classes (A, B, C) which have OneToMany relationships
A <>-- B <>-- C
code:
@Getter @Setter
@Entity
public class A {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "a")
private List<B> bList;
private String name;
}
@Getter @Setter
@Entity
public class B {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
private A a;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "b")
private List<C> cList;
private String name;
}
@Getter @Setter
@Entity
public class C {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.EAGER)
private B b;
}
How should I properly populate the data? When I'm trying something like this:
A a = new A();
a.setName("A-1");
aRepository.save(a);
B b1 = new B();
b1.setName("B-1");
b1.setA(a);
bRepository.save(b1);
B b2 = new B();
b2.setName("B-2");
b2.setA(a);
bRepository.save(b2);
for (int i = 1; i <= 9; i++ ) {
C c = new C();
c.setName("C-"+i);
c.setB(b1);
cRepository.save(c);
}
I get properly populated data in the database:
+----+------+
| ID | NAME |
+----+------+
| 1 | A-1 |
+----+------+
+----+------+------+
| ID | NAME | A_ID |
+----+------+------+
| 1 | B-1 | 1 |
+----+------+------+
| 2 | B-2 | 1 |
+----+------+------+
+----+------+------+
| ID | NAME | B_ID |
+----+------+------+
| 1 | C-1 | 1 |
+----+------+------+
| 2 | C-2 | 1 |
+----+------+------+
| 3 | C-3 | 1 |
+----+------+------+
| 4 | C-4 | 1 |
+----+------+------+
| 5 | C-5 | 1 |
+----+------+------+
| 6 | C-6 | 1 |
+----+------+------+
| 7 | C-7 | 1 |
+----+------+------+
| 8 | C-8 | 1 |
+----+------+------+
| 9 | C-9 | 1 |
+----+------+------+
But when I'm trying to fetch data from the repository something is wrong:
These tests are ok:
assertThat(cRepository.findOne(1l)).isNotNull();
assertThat(cRepository.findOne(1l).getB()).isNotNull();
assertThat(cRepository.findAll()).hasSize(9);
assertThat(bRepository.findAll()).hasSize(2);
assertThat(bRepository.findOne(1l).getCList().size()).isEqualTo(9);
but this one is failing:
assertThat(aRepository.findOne(1l).getBList().size()).isEqualTo(2);
It returns 10 records. Query SELECT * FROM B WHERE A_ID = 1
returns 2 records, so could you please shed some light on what i am doing wrong?
Upvotes: 2
Views: 1617
Reputation: 2931
Its strange hibernate behaviour which results from the fact that EAGER type using OUTER JOINS. You get duplicate B objects for each of C objects related with B. I had similiar problem and the only solution i found for that is change fetchType to LAZY or change List to Set.
Here you have better explanation: Hibernate Criteria returns children multiple times with FetchType.EAGER or Duplicates in OneToMany annotated List
Upvotes: 2
Reputation: 5503
You need to change it to the following:
B b1 = new B();
b1.setName("B-1");
b1.setA(a);
a.getBList().add(b1);
bRepository.save(b1);
B b2 = new B();
b2.setName("B-2");
b2.setA(a);
a.getBList().add(b2);
bRepository.save(b2);
aRepository.save(a);
Now a bit side information from my side. In theory it works also without the three extra lines, but only if you invalidate the hibernate cache. I think maybe it would return the correct result if you detach the a and dann get the a. General advice, avoid bidirectional mappings if possible. If you need it add the follwing in a
public void addB(B b) {
b.setA(this);
a.getBList().add(b);
}
Releated to the ManyToOne. Theres no need to add eager fetching because thats the default value.
Upvotes: 0