PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Mapowanie wielu wierszy JPA za pomocą ElementCollection

Niestety, myślę, że problemem jest niewielka różnica polegająca na tym, że trzymasz tylko jeden stół.

Spójrz na deklarację PhoneId klasa (która sugerowałbym lepiej nazywać się PhoneOwner lub coś takiego):

@Entity
@Table(name="Phones")
public class PhoneId {

Kiedy deklarujesz, że klasa jest jednostką odwzorowaną na określoną tabelę, tworzysz zestaw asercji, z których dwa są tutaj szczególnie ważne. Po pierwsze, że w tabeli jest jeden wiersz dla każdej instancji jednostki i na odwrót. Po drugie, że w tabeli jest jedna kolumna dla każdego pola skalarnego encji i na odwrót. Oba te elementy leżą u podstaw idei mapowania obiektowo-relacyjnego.

Jednak w twoim schemacie żadne z tych twierdzeń nie ma racji bytu. W danych, które podałeś:

OWNER_ID    TYPE      NUMBER
  1         home      792-0001
  1         work      494-1234
  2         work      892-0005

Istnieją dwa wiersze odpowiadające encji o owner_id 1, z naruszeniem pierwszego twierdzenia. Istnieją kolumny TYPE i NUMBER które nie są mapowane na pola w encji, naruszając drugie stwierdzenie.

(Aby było jasne, nie ma nic złego w deklaracji Phone klasa lub phones pole - tylko PhoneId podmiot)

W rezultacie, gdy dostawca JPA próbuje wstawić instancję PhoneId do bazy danych, wpada w kłopoty. Ponieważ nie ma mapowań dla TYPE i NUMBER kolumny w PhoneId , gdy generuje kod SQL dla wstawienia, nie zawiera dla nich wartości. Dlatego pojawia się błąd, który widzisz — dostawca zapisuje INSERT INTO Phones (owner_id) VALUES (?) , które PostgreSQL traktuje jako INSERT INTO Phones (owner_id, type, number) VALUES (?, null, null) , który został odrzucony.

Nawet jeśli zdołasz wstawić wiersz do tej tabeli, będziesz miał kłopoty z odzyskaniem z niej obiektu. Załóżmy, że prosiłeś o wystąpienie PhoneId z owner_id 1. Dostawca napisałby SQL o wartości select * from Phones where owner_id = 1 , i oczekuje, że znajdzie dokładnie jeden wiersz, który może zamapować na obiekt. Ale znajdzie dwa rzędy!

Obawiam się, że rozwiązaniem jest użycie dwóch tabel, jednej dla PhoneId , a jeden dla Phone . Tabela dla PhoneId będzie banalnie prosty, ale jest niezbędny do prawidłowego działania maszyn JPA.

Zakładając, że zmienisz nazwę PhoneId do PhoneOwner , tabele muszą wyglądać tak:

create table PhoneOwner (
    owner_id integer primary key
)

create table Phone (
    owner_id integer not null references PhoneOwner,
    type varchar(255) not null,
    number varchar(255) not null,
    primary key (owner_id, number)
)

(Zrobiłem (owner_id, number) klucz podstawowy dla Phone , przy założeniu, że jeden właściciel może mieć więcej niż jedną liczbę danego typu, ale nigdy nie będzie miał jednej liczby zapisanej w dwóch rodzajach. Możesz preferować (owner_id, type) jeśli to lepiej odzwierciedla Twoją domenę).

Podmioty są wtedy:

@Entity
@Table(name="PhoneOwner")
public class PhoneOwner {
    @Id
    @Column(name="owner_id")
    long id;

    @ElementCollection
    @CollectionTable(name = "Phone", joinColumns = @JoinColumn(name = "owner_id"))
    List<Phone> phones = new ArrayList<Phone>();
}

@Embeddable
class Phone {
    @Column(name="type", nullable = false)
    String type;
    @Column(name="number", nullable = false)
    String number;
}

Teraz, jeśli naprawdę nie chcesz wprowadzać tabeli dla PhoneOwner , możesz się z niego wydostać za pomocą widoku. Tak:

create view PhoneOwner as select distinct owner_id from Phone;

O ile dostawca JPA może powiedzieć, jest to tabela i będzie obsługiwać zapytania, które musi wykonać, aby odczytać dane.

Jednak nie obsługuje wstawek. Jeśli kiedykolwiek musiałeś dodać telefon dla właściciela, który nie znajduje się obecnie w bazie danych, musisz przejść do tyłu i wstawić wiersz bezpośrednio do Phone . Niezbyt ładne.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Czy odniesienia do kluczy obcych mogą zawierać wartości NULL w PostgreSQL?

  2. Importowanie CSV z przecinkami w wartościach ciągu

  3. Oracle DBMS_LOB.WRITEAPPEND do konwersji Postgres

  4. Migracja Flyway z java

  5. Czy możemy wchodzić w interakcję ze skryptem psql?