sql >> Databáze >  >> RDS >> PostgreSQL

Mapování více řádků JPA pomocí ElementCollection

Bohužel si myslím, že malý rozdíl v tom, že si necháte jen jeden stůl, je problém.

Podívejte se na deklaraci PhoneId třída (kterou bych navrhoval, aby se lépe nazývala PhoneOwner nebo něco takového):

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

Když deklarujete, že třída je entita mapovaná do určité tabulky, vytváříte sadu tvrzení, z nichž dvě jsou zde obzvláště důležitá. Za prvé, že v tabulce existuje jeden řádek pro každou instanci entity a naopak. Za druhé, že pro každé skalární pole entity je v tabulce jeden sloupec a naopak. Oba tyto jsou jádrem myšlenky objektově-relačního mapování.

Ve vašem schématu však neplatí ani jedno z těchto tvrzení. V údajích, které jste uvedl:

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

Entitě s owner_id odpovídají dva řádky 1, v rozporu s prvním tvrzením. Jsou zde sloupce TYPE a NUMBER které nejsou mapovány na pole v entitě, což porušuje druhé tvrzení.

(Aby bylo jasno, na vašem prohlášení o Phone není nic špatného třída nebo phones pole – pouze PhoneId entity)

Výsledkem je, že když se váš poskytovatel JPA pokusí vložit instanci PhoneId do databáze, dostane se do potíží. Protože pro TYPE neexistují žádná mapování a NUMBER sloupce v PhoneId , když generuje SQL pro vložení, nezahrnuje pro ně hodnoty. Proto se zobrazí chyba, kterou vidíte – poskytovatel zapíše INSERT INTO Phones (owner_id) VALUES (?) , které PostgreSQL považuje za INSERT INTO Phones (owner_id, type, number) VALUES (?, null, null) , což je zamítnuto.

I kdyby se vám podařilo vložit řádek do této tabulky, narazili byste na potíže při načítání objektu z ní. Řekněme, že jste požádali o instanci PhoneId s owner_id 1. Poskytovatel zapíše SQL ve výši select * from Phones where owner_id = 1 , a očekával by, že najde právě jeden řádek, který může mapovat na objekt. Ale najde dva řádky!

Řešením je, obávám se, použít dvě tabulky, jednu pro PhoneId a jeden pro Phone . Tabulka pro PhoneId bude triviálně jednoduchý, ale je nezbytný pro správný chod strojů JPA.

Za předpokladu, že přejmenujete PhoneId na PhoneOwner , tabulky musí vypadat takto:

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)
)

(Vytvořil jsem (owner_id, number) primární klíč pro Phone , za předpokladu, že jeden vlastník může mít více než jedno číslo daného typu, ale nikdy nebude mít jedno číslo evidované pod dvěma typy. Můžete preferovat (owner_id, type) pokud to lépe odpovídá vaší doméně.)

Entity jsou pak:

@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;
}

Nyní, pokud opravdu nechcete zavést tabulku pro PhoneOwner , pak se z toho možná budete moci dostat pomocí pohledu. Takhle:

create view PhoneOwner as select distinct owner_id from Phone;

Pokud poskytovatel JPA může říci, jedná se o tabulku a bude podporovat dotazy, které potřebuje ke čtení dat.

Nepodporuje však vložky. Pokud byste někdy potřebovali přidat telefon pro vlastníka, který aktuálně není v databázi, museli byste přejít zadní část a vložit řádek přímo do Phone . Ne moc hezké.




  1. Transakce Codeigniter

  2. Jak zkrátit dobu provádění dotazu pro tabulku s velkými daty

  3. Tipy pro správu zálohování pro TimescaleDB

  4. Je možné uložit hodnotu jednoho vybraného sloupce a použít ji pro další?