sql >> Databasteknik >  >> RDS >> PostgreSQL

JPA-kartläggning av flera rader med ElementCollection

Tyvärr tror jag att den lilla skillnaden att du bara har ett bord är problemet här.

Titta på deklarationen för PhoneId klass (som jag skulle föreslå är bättre kallad PhoneOwner eller något liknande):

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

När du deklarerar att en klass är en entitet som är mappad till en viss tabell, gör du en uppsättning påståenden, varav två är särskilt viktiga här. För det första att det finns en rad i tabellen för varje instans av entiteten, och vice versa. För det andra att det finns en kolumn i tabellen för varje skalärt fält i entiteten, och vice versa. Båda dessa är kärnan i idén om objektrelationell kartläggning.

Men i ditt schema håller inget av dessa påståenden. I uppgifterna du angav:

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

Det finns två rader som motsvarar entiteten med owner_id 1, strider mot det första påståendet. Det finns kolumner TYPE och NUMBER som inte är mappade till fält i entiteten, vilket bryter mot det andra påståendet.

(För att vara tydlig är det inget fel med din deklaration av Phone klass eller phones fältet - bara PhoneId enhet)

Som ett resultat, när din JPA-leverantör försöker infoga en instans av PhoneId in i databasen får den problem. Eftersom det inte finns några mappningar för TYPE och NUMBER kolumner i PhoneId , när den genererar SQL för infogningen, inkluderar den inte värden för dem. Det är därför du får felmeddelandet du ser - leverantören skriver INSERT INTO Phones (owner_id) VALUES (?) , som PostgreSQL behandlar som INSERT INTO Phones (owner_id, type, number) VALUES (?, null, null) , vilket avvisas.

Även om du lyckades infoga en rad i den här tabellen, skulle du få problem med att hämta ett objekt från den. Säg att du bad om instansen PhoneId med owner_id 1. Leverantören skulle skriva SQL motsvarande select * from Phones where owner_id = 1 , och den skulle förvänta sig att hitta exakt en rad, som den kan mappa till ett objekt. Men den kommer att hitta två rader!

Lösningen är, är jag rädd, att använda två tabeller, en för PhoneId , och en för Phone . Tabellen för PhoneId kommer att vara trivialt enkelt, men det är nödvändigt för att JPA-maskineriet ska fungera korrekt.

Förutsatt att du byter namn på PhoneId till PhoneOwner , tabellerna måste se ut så här:

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

(Jag har gjort (owner_id, number) den primära nyckeln för Phone , under antagandet att en ägare kan ha mer än ett nummer av en given typ, men kommer aldrig att ha ett nummer registrerat under två typer. Du kanske föredrar (owner_id, type) om det bättre återspeglar din domän.)

Entiteterna är då:

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

Nu, om du verkligen inte vill introducera en tabell för PhoneOwner , då kanske du kan ta dig ur det med en vy. Så här:

create view PhoneOwner as select distinct owner_id from Phone;

Såvitt JPA-leverantören kan se är detta en tabell och den kommer att stödja de frågor den behöver göra för att läsa data.

Det kommer dock inte att stödja inlägg. Om du någonsin behövt lägga till en telefon för en ägare som för närvarande inte finns i databasen, måste du gå runt baksidan och infoga en rad direkt i Phone . Inte särskilt trevligt.




  1. MySQL-val för uppdatering returnerar tom uppsättning även om det finns en rad

  2. Återställ ROOT-lösenord i MySQL 5.6

  3. Hur kopierar man endast de första 4000 tecknen från Oracle NCLOB till NVARCHAR2(4000)-värde?

  4. Hur justify_hours() fungerar i PostgreSQL