2013-06-20 23 views
5

Jest to związane z this question, ale poniższy przykład jest krótszy, więc pomyślałem, że inne pytanie na ten temat miałoby sens.Play + Ebean + JPA: Kaskada usuwanie na mapowaniu OneToOne

Mam dwie jednostki, A i B, w relacji jeden-do-jednego. Dla A, B jest opcjonalne, a każdy B musi mieć A. Chcę kaskady usuwa z punktu A do B. Oto moja pierwsza próba:

@Entity 
public class A extends Model { 

    @Id 
    private Long id; 

    @OneToOne(optional = true, mappedBy = "a", cascade = CascadeType.REMOVE, orphanRemoval = true) 
    private B b; 

} 

@Entity 
public class B extends Model { 

    @Id 
    private Long id; 

    @OneToOne(optional = false) 
    private A a; 

} 

Jednak wydaje się, że Ebean ignoruje „opcjonalne” adnotacji, bo kiedy wykonać znaleźć dla zapisanego a o identyfikatorze 1 dodaje SQL jest wykonywany:

select t0.id c0, t1.id c1 
from a t0 
join b t1 on t1.a_id = t0.id 
where t0.id = 1 

innymi słowy, to robi wewnętrzną zamiast lewej przyłączyć, co powoduje, że znalezisko na niepowodzenie, gdy nie ma skojarzonego B. Próbowałem różnych kombinacji @JoinColumn itp. bez rezultatu. Jedynym dość satysfakcjonującym obejściem jest model A-to-B jako "fałszywa" relacja jeden-do-wielu. Czy istnieje lepsze rozwiązanie? Czy jest to błąd, czy jest to znane/stwierdzone ograniczenie Ebean?

Odpowiedz

1

Znalazłem rozwiązanie. Zmieniłem kierunek tego mapowania. Usunąłem więc mappedBy = "a" z klasy A i dodałem mappedBy = "b" w klasie B.
Więc kod teraz wygląda w ten sposób:

@Entity 
public class A extends Model { 

    @Id 
    private Long id; 

    @OneToOne(optional = true, cascade = CascadeType.REMOVE, orphanRemoval = true) 
    private B b; 

... 
} 


@Entity 
public class B extends Model { 

    @Id 
    private Long id; 

    @OneToOne(optional = false, mappedBy = "b") 
    private A a; 

    private String name; 

    ... 
} 

I dodaną name pole w klasie B, aby badanie to bardziej interesujące.

Moje testy medhod:

@Test 
public void abTest() { 
    FakeApplication app = Helpers.fakeApplication(Helpers.inMemoryDatabase()); 
    Helpers.start(app); 

    A a = new A(); 
    B b = new B(); 
    a.setId(1L); 
    b.setId(2L); 
    a.setB(b); 
    b.setA(a); 
    b.setName("bbb"); 

    Ebean.save(b); 
    Ebean.save(a); 

    A fa = Ebean.find(A.class, 1L); 
    System.out.println("a.id: "+fa.getId()); 
    System.out.println("a.b.id: "+fa.getB()); 
    System.out.println("a.b.name: "+fa.getB().getName()); 

    A a1 = new A(); 
    a1.setId(3L); 
    Ebean.save(a1); 
    A fa1 = Ebean.find(A.class, 3L); 
    System.out.println("a1.id: "+fa1.getId()); 
    System.out.println("a1.b.id: "+fa1.getB()); 

    B fb = Ebean.find(B.class, 2L); 
    System.out.println("b.id: "+fb.getId()); 
    System.out.println("b.name: "+fb.getName()); 
    System.out.println("b.a.id: "+fb.getA().getId()); 
} 

A wynik tego badania jest:

[debug] c.j.b.PreparedStatementHandle - insert into b (id, name) values (2,'bbb') 
[debug] c.j.b.PreparedStatementHandle - insert into a (id, b_id) values (1,2) 
[debug] c.j.b.PreparedStatementHandle - select t0.id c0, t0.b_id c1 from a t0 where t0.id = 1 
a.id: 1 
a.b.id: [email protected] 
[debug] c.j.b.PreparedStatementHandle - select t0.id c0, t0.name c1, t1.id c2 from b t0 left outer join a t1 on t1.b_id = t0.id where t0.id = 2 
a.b.name: bbb 
[debug] c.j.b.PreparedStatementHandle - insert into a (id, b_id) values (3,'[SQL NULL of type -5]') 
[debug] c.j.b.PreparedStatementHandle - select t0.id c0, t0.b_id c1 from a t0 where t0.id = 3 
a1.id: 3 
a1.b.id: null 
[debug] c.j.b.PreparedStatementHandle - select t0.id c0, t0.name c1, t1.id c2 from b t0 left outer join a t1 on t1.b_id = t0.id where t0.id = 2 
b.id: 2 
b.name: bbb 
b.a.id: 1 

Więc ten kod działa dobrze bez względu na to, czy A.b jest null czy nie. Jak widać w dzienniku jest teraz left outer join zamiast join.