Artur Świerc

Artur Świerc Programista PHP/Java

Temat: JPA oraz @OneToMany

Witam

Dla przykładu mam tabelki (w skróconej formie)


Customer: ID | FIRST_NAME | LAST_NAME
Phone: ID | NUMBER | CUSTOMER_ID


Chciałbym teraz zrobić relację jeden do wielu - wiadomo jeden Customer ma wiele telefonów, z tym że CUSTOMER_ID jest w tabelce Phone.

Problem w tym, że chciałbym aby encja Customer była stroną właścicielską tego modelu - czyli wykonując metodę "persist" na Customer, w bazie zapisywała by się jednocześnie encja Phone.

W książce z której się uczę jest następujący przykład:

Encja Phone:
 
@Entity
public class Phone implements java.io.Serializable {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ID")
private int id;

@Column(name="NUMBER")
private String number;

...


Encja Customer:

 
@Entity
public class Customer implements Serializable {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ID")
private int id;

@Column(name="FIRST_NAME")
private String firstName;

@Column(name="LAST_NAME")
private String lastName;

@OneToMany(cascade={CascadeType.ALL})
@JoinColumn(name="CUSTOMER_ID")
private Collection<Phone> phoneNumbers = new ArrayList<Phone>();

public Collection<Phone> getPhoneNumbers() {
return phoneNumbers;
}

public void setPhoneNumbers(Collection<Phone> phones) {
this.phoneNumbers = phones;
}
...


Przykład z książki jest chyba jakiś lewy, wg mnie adnotacja @JoinColumn w encji Customer wskazuje na kolumnę CUSTOMER_ID w tabeli Customer, a nie w tabeli Phone.

Do tego kompilator sypie wyjątkami:

Exception Description: @OneToMany for attribute name [phoneNumbers] in entity class [class com.titan.domain.Customer] should not have @JoinColumn(s) specified. In the case where the @OneToMany is not mapped by another entity (that is, it is the owning side and is uni-directional), it should specify (optional through defaulting) a @JoinTable.
...


Mógłbym to zrobić w inny sposób, czyli dodając @ManyToOne oraz @JoinColumn(name="CUSTOMER_ID") w encji Phone, jednocześnie mapować to pole w encji Customer. Ale wtedy encja Customer chyba straciła by status encji właścicielskiej (?).

Pozdr.

/ps/ Książka to "Enterprise JavaBeans 3.0"Artur Świerc edytował(a) ten post dnia 21.11.09 o godzinie 11:19

konto usunięte

Temat: JPA oraz @OneToMany

może tak?:

Customer

@OneToMany(cascade = CascadeType.ALL, mappedBy = "customer")
private Collection<Phone> phoneCollection;


Phone

@JoinColumn(name = "customer_id", referencedColumnName = "id")
@ManyToOne(optional = false)
private Customer customer;
Paweł Ryznar edytował(a) ten post dnia 21.11.09 o godzinie 11:47

konto usunięte

Temat: JPA oraz @OneToMany

Też mi się wydaje że tak powinno być. Adnotacja JoinColumn odnosi sie do kolumny w tabeli Phone która jest kluczem obcym. W tym wypadku CUSTOMER_ID. A atrybut mappedBy wskazuje na encje która jest właścicielką. Czyli uskuteczniając persist na encji Customer zapisywało się bedzie customer ze swoimi telefonami:)
Artur Świerc

Artur Świerc Programista PHP/Java

Temat: JPA oraz @OneToMany

Bardzo dziękuję! Takie rozwiązanie działa tj powinno :)

Zapomniałem też o tym, że mappedBy wskazuje na właściciela i myślałem, że w takim rozwiązaniu będę musiał robić persist na phone :P

Swoją drogą trapi mnie nadal przykład z książki - dlaczego nie podali rozwiązania Pawła, a jedynie błędne.

Temat: JPA oraz @OneToMany

Według mnie przykład z książki jest jak najbardziej dobry a nawet bardziej logiczny. Różnica jest taka, że Paweł utworzył relację dwukierunkową a w książce była jednokierunkowa. Napisałem, że wersja książkowa jest bardziej logiczna, bo trudno mi znaleźć przykład, gdzie mając telefon chciałbym znać jego właściciela (to nie jest realny świat, tylko program komputerowy - on nie zgubi telefonu w tramwaju;) ).

Trzeba zauważyć, że w przykładzie z książki w encji(klasie) Phone nie ma pola Customer. Dla relacji OneToMany JPA domyślnie tworzy nową tabelę łączącą dwie encje. Gdyby pominąć adnotację @JoinColumn mielibyśmy 3 tabele:


CUSTOMER(ID, FIRST_NAME, LAST_NAME)
CUSTOMER_PHONE(CUSTOMER_ID, PHONE_ID)
PHONE(ID, NUMBER)


Adnotacja @JoinColumn mówi o tym, żeby kolumnę łączącą (CUSTOMER_ID) umieścić w tej samej tabeli co obiekt do którego się odnosimy. Stąd właśnie w tabeli Phone mamy CUSTOMER_ID:

CUSTOMER(ID, FIRST_NAME, LAST_NAME)
PHONE(ID, NUMBER, CUSTOMER_ID)


W ogólności to jedno i drugie rozwiązanie jest dobre, zależy tylko od zastosowania.
Aleksander Lech

Aleksander Lech Architekt rozwiązań

Temat: JPA oraz @OneToMany

Dokładnie w JPA 1.0 do relacji jednokierunkowej @OneToMany wymagana jest osobna tabela (niestety). Tej niedogodności nie ma w wersji 2.0 specyfikacji gdzie w owej relacji możemy zastosować adnotacje @JoinColumn...

Następna dyskusja:

JPA - zapytania składane




Wyślij zaproszenie do