2

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?

trzeciakp
  • 109
  • 1
  • 9

2 Answers2

2

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

Community
  • 1
  • 1
jgr
  • 2,831
  • 2
  • 15
  • 28
0

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.

gunr2171
  • 16,104
  • 25
  • 61
  • 88
mh-dev
  • 5,264
  • 4
  • 25
  • 23
  • Apparently it does not work. I still get 10 records. 1) How is it supposed to work? I thought that bidirectional relationship is updated when I save object to repository. Indeed the database is updated, but somehow in the code it is not. 2) Why a.bList is updated several times by adding the same object (B-1)? When I fetch object A-1 from repository then the cList contains 9x object B-1 and one object B-2. Why when I save C-x object, the B-1 is added to A-1 cList? – trzeciakp May 11 '15 at 18:50
  • gunr2171 has already edited it, it was just a typo sry for that. – mh-dev May 11 '15 at 18:57
  • for the 10 enty problem see jgr's answer. To be honest I missed that with 10 and stopped reading at the assertThat – mh-dev May 11 '15 at 19:00