1. Entity

1.1. Begriffe

Primärschlüssel (@Id) können durch die Datenbank generiert werden (@GeneratedValue).

Strategien sind: Identity, Table, Sequence und Auto.

Auto: Default, wenn nichts angegeben wird. Bei Hibernate Auto = Sequence.

Sequence: Neue Ids werden über eine Sequence generiert, vielfältige. Konfigurationsmöglichkeiten:

Identity: Auto-Increment Spalte.

Table: Tabelle "emuliert" Sequence.

1.2. Code Bsp

Entity Code
@Entity
public class Employee {
     @Id
     @GeneratedValue(strategy = GenerationType.AUTO)
     Long id;
     String name;
     long salary;
     public Employee() {}
}

2. Entity Manager

2.1. Begriffe

Der EntityManager verwaltet die Entitäten.

Er synchronisiert die Objekt- und Datenbankidentität von verwalteten Entitäten automatisch.

Erst zum Commit-Zeitpunkt werden Änderungen an verwalteten Objekten (Entities) in die relationale Datenbank übertragen.

2.1.1. Wichtige Methoden:

  • persist

  • merge

  • refresh

  • remove

2.2. Persistierbare Datentypen

Was kann man Persistieren?

  • Primitive Datentypen (int, char, …​) und Wrapper-Objekte (Integer, ..)

  • Strings

  • BigInteger und BigDecimal

  • Date und Calendar

  • Collections und Arrays von persistierbaren Typen

  • Enumerations

  • Entity-Typen und Collections von Entitytypen

  • Eingebettete Klassen

  • Serialisierbare Datentypen (→ Blob)

3. Mapping

Es wird immer vom Default-Verhalten ausgegangen (Convention over Configuration).

Das Default-Verhalten kann aber angepasst werden:

@Entity
@Table(name = "EMP")
public class Employee {
    @Id
    @Column(name = "EMP_ID")
    private String name;
}

3.1. Arten von Mapping

  • Klassename = Tabellenname - @Table

  • Property-Name = Spaltenname - @Column

  • String auf VARCHAR - @Column(length=20)

  • Ausnahmen sind anzugeben (nicht die Regel)

  • @Column(columnDefinition="varchar2(20)")

  • Andere Namen @Column(name = "myName")

  • Nicht persistente Properties - @Transient

  • NOT NULL - @Column(nullable = false)

4. Beziehungen

4.1. Beziehungen zwischen Tabellen

3 Arten:

  • 1:1

  • 1:n

  • n:m

4.2. Möglichkeiten zur Navigation im Klassenmodel

diagram classes 1
Figure 1. Bidirektional
diagram classes 2
diagram classes 3
Figure 2. Unidirektional

4.3. 1:1 Beziehung unidirektional

Code
@Entity
public class Employee {
    @Id
    @GeneratedValue
    Integer id;

    int salary;
    String firstName;
    String lastName;

    @OneToOne
    Address address;
}

@Entity
public class Address {
    @Id
    @GeneratedValue
    Integer id;
    String street;
    @Column(name = "addrNum")
    String number;
    String zipCode;
    String city;
}
diagram classes 4
Figure 3. cld

Es müssen alle miteinander in Beziehung stehenden Entitäten im Zustand "managed" sein:

Managed
Employee emp = new Employee(800, "Hermann", "Mayr");
Address a1 = new Address("Himbeerstrasse", "22a", "4440", "Steyr");
emp.setAddress(a1);
em.persist(a1);
em.persist(emp);

Standardmäßig wird bei unidirektionalen Beziehungen eine Assoziativtabelle angelegt / verwendet (da Address keine Foreign-KeySpalte besitzt).

Ist dies nicht erwünscht (meistens), kann JPA durch die Annotation @JoinColumn angewiesen werden, eine Foreign-Key-Spalte in der Tabelle der referenzierenden Entität anzulegen:

Annotation
@JoinColumn(name = "addressid")
Address address;

4.4. Cascade

Transitive Persistenz (Persistence by Reachability) bedeutet, dass ein Entity nicht nur explizit persistent gemacht werden kann, sondern dass es implizit (automatisch) persistent wird, wenn es von einem bereits persistenten Entity referenziert wird.

Mit dem Attribut cascade kann angegeben werden, dass eine Operation am einen Ende der Beziehung auch für die Entität(en) am anderen Ende wirksam wird.

Beispiel: Wird Employee persistiert, so geschieht dies auch mit der referenzierten Entität Address automatisch:

Annotation
@OneToOne(cascade = CascadeType.PERSIST)
Address address;

Employee emp = new Employee(800, "Hermann", "Mayr");
Address a1 = new Address("Himbeerstrasse", "22a", "4440", "Steyr");
emp.setAddress(a1);

//em.persist(a1); a1 wird von emp referenziert und somit automatisch persistiert
em.persist(emp);

4.5. 1:1 Beziehung bidirektional

Über das Attribut mappedBy das andere "Ende" einer bidirektionalen Beziehung festgelegt werden.

mappedBy bezieht sich dabei auf die Membervariable.

Eine Foreign-Key-Spalte wird automatisch angelegt, kann allerdings weiterhin mit @JoinColumn konfiguriert werden.

Code
@Entity
public class Employee {
    @Id
    @GeneratedValue
    Integer id;

    int salary;
    String firstName;
    String lastName;

    @OneToOne
    Address address;
    }


@Entity
public class Address {
    @Id
    @GeneratedValue
    Integer id;
    String street;
    @Column(name = "addrNum")
    String number;
    String zipCode
    String city;
    @OneToOne(mappedBy = "address")
    Employee employee;
}
diagram classes 5 png
Figure 4. cld

4.6. 1:n Beziehung unidirektional

  • @OneToMany

  • Das n-Ende wird über eine Collection (Set, List) abgebildet

  • Wie bei @OneToOne wird standardmäßig eine Assoziativtabelle angelegt, dies kann durch Angabe einer @JoinColumn verhindert werden.

Tipp: Collection im Kontruktor initialisieren
Code
@Entity
public class Employee {
    @Id
    @GeneratedValue
    Integer id;

    int salary;
    String firstName;
    String lastName;

    @OneToOne
    Address address;
    }


@Entity
public class Division {
 @Id
 @GeneratedValue
 Integer id;
 String name;

 @OneToMany
 List<Employee> employees;
 public Division() {
 employees = new ArrayList<>();
 }
diagram classes 6 png
Figure 5. cld

4.7. 1:n Beziehung - Tipps

Da bidirektionale 1:n-Beziehungen sehr häufig vorkommen, lohnt es sich, eine einfache Merkregel einzuprägen:

  • Bei bidirektionalen 1:n-Beziehungen (oder n:1) ist die „nSeite" die besitzende Seite

  • Die Join-Spalte der besitzenden Seite wird optional mit @Joinnolunn annotiert

  • Die „1-Seite" ist die inverse Seite, daher wird des mappedByAttribut verwendet

4.8. m:n Beziehung unidirektional

Code
@Entity
@Entity
public class Employee {
     @Id
     @GeneratedValue
     Integer id;

     int salary;
     String firstName;
     String lastName;

     @ManyToMany
     List<Task> tasks;
}
diagram classes 7 png
Figure 6. cld
Code
Employee emp = new Employee(800, "Hermann", "Mayr");
Task t = new Task();
emp.getTasks().add(t);

4.9. m:n Beziehung bidirektional

Code
@Entity
public class Employee {
    @Id
    @GeneratedValue
    Integer id;

    int salary;
    String firstName;
    String lastName;

    @ManyToMany
    List<Task> tasks;
}

@Entity
public class Task {
    @Id
    @GeneratedValue
    int id;

    String name;
    int bonus;

    @ManyToMany(mappedBy = "tasks")
    List<Employee> employees;
}
diagram classes 8 png
Figure 7. cld
Code
Employee emp = new Employee(800, "Hermann", "Mayr");
Task t = new Task();
emp.getTasks().add(t);
t.getEmployees().add(emp);

5. Vererbung

Bisher direktes Mapping zwischen Objektmodell und relationalem Modell möglich:

  • Klasse → Tabelle

  • Membervariable → Spalte

  • Objekt → Zeile

  • Beziehungen über FK (+ ev. Assoziativtabelle)

3 verschiedene Abbildungen werden angeboten, mit unterschiedlichen Stärken/Schwächen:

  • SINGLE_TABLE

  • JOINED

  • TABLE_PER_CLASS

5.1. InheritanceType.SINGLE_TABLE

  • Es wird eine Tabelle mit allen Attributen für sämtliche Klassen angelegt

  • Typ wird in Spalte DTYPE gespeichert

  • Spalten, für welche es beim aktuellen Typ keine Entsprechung gibt (z.B. Spalte internShipEnd bei Manager) werden auf NULL gesetzt

SINGLE TABLE

Vorteile:

  • Queries typischerweise effizient → schnel

Nachteile:

  • Bei großen Hierarchien oft viele "null-Spalten"

  • Keine Not-Null-Constraints bei Variablen in abgeleiteten Klassen

5.2. InheritanceType.TABLE_PER_CLASS

Für jede Klasse in der Hierarchie wird eine Tabelle mit den in dieser Klasse vorhandenen Properties angelegt.

Vorteile:

  • Nur tatsächlich vorhandene Properties werden auf Tabelle abgebildet (keine null-Spalten)

  • Not-Null Constraints möglich *Queries auf nur einen Typ sehr schnell:

SQL Code
Select i from Intern i where salary > 2000

Nachteile:

  • Abfragen über mehrere Typen / Basisklasse langsam / schnell sehr komplex

SQL Code
Select e from Employee where salary > 2000
TABLE PER CLASS

5.3. InheritanceType.JOINED

  • Je Klasse eine Tabelle

  • Tabellen von abgeleitete Klassen enthalten keine Spalten für geerbte Attribute

    • Nur neue Membervariablen + ID

  • Bei Abfragen → JOIN über Spalte ID

Vorteile:

  • Nur tatsächlich vorhandene Properties werden auf Tabelle abgebildet (keine null-Spalten)

  • Not-Null Constraints möglich

  • Queries auf nur einen Typ sehr schnell:

SQL Code
Select i from Intern i where salary > 2000

Nachteile:

  • Abfragen über mehrere Typen / Basisklasse langsam / schnell sehr komplex

SQL Code
Select e from Employee where salary > 2000
JOINED

5.4. Wann welche Strategie?

SINGLE_TABLE, falls Query-Performance wichtig ist und (viele) polymorphe Abfragen vorkommen (Abfragen, welche mehr als einen Typ zurückliefern können). Allerdings verliert man dabei die Möglichkeit, not-null Constraints auf Membervariablen von abgeleiteten Klassen zu setzen.

JOINED, falls not-null Constraints zum Einsatz kommen sollen und es polymorphe Abfragen gibt. JOINED macht ebenfalls Sinn, wenn der Vererbungsbaum nicht balanziert ist (nahezu alle Felder in einer Subklasse, fast nur Instanzen eines Typs).

TABLE_PER_CLASS, falls es keine polymophen Abfragen gibt.

6. @Embeddable / @Embedd

Problemstellung:

Feingranulares Objektmodell (viele Entitäten) führt zu vielen Tabellen → schlechte Performanz (Joins, Selects, Speicher-Overhead)

Abhilfe:

@Embeddable / @Embedd erlaubt das "Einbetten" von Objekten direkt in die Eltern-Entität

Membervariablen des eingebetteten Objekts werden Spalten in der Tabelle der ElternEntität

Kurzgefasst: Wie Inject bei Quarkus.

6.1. Definition:

Definition
@Embeddable
public class EmbeddableEntity {
 int x = 0;
 int y = 0;
}

6.2. Verwendung:

Verwendung
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Employee {
    @Id
    @GeneratedValue
    long id;
    @Embedded
    EmbeddableEntity someEnt;
    public Employee() {
    someEnt = new EmbeddableEntity();
}

6.3. Zu beachten:

  • Nur für gerichtete @OneToOne Beziehungen möglich

  • Eingebettetes Objekt existiert im Lebenszyklus der Eltern-Entität

  • Laden des eingebetteten Objekts nur über ElternEntität möglich

  • @Embedd vollständig transparent in JPQL:

SQL Code
Select e from Employee e where e.someEnt.x=0
  • Eingebettes Objekt muss initialisiert sein (null unzulässig), sonst:

Exception in thread "main" javax.persistence.PersistenceException:
org.hibernate.PropertyValueException: not-null property references a null or
transient value : jpademo.entities.Employee.someEnt

6.4. Ergebnis:

ERGEBNIS