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.
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.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.3. 1:1 Beziehung unidirektional
@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;
}

Es müssen alle miteinander in Beziehung stehenden Entitäten im Zustand "managed" sein:
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:
@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:
@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.
@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;
}

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

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
@Entity
@Entity
public class Employee {
@Id
@GeneratedValue
Integer id;
int salary;
String firstName;
String lastName;
@ManyToMany
List<Task> tasks;
}

Employee emp = new Employee(800, "Hermann", "Mayr");
Task t = new Task();
emp.getTasks().add(t);
4.9. m:n Beziehung bidirektional
@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;
}

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
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:
Select i from Intern i where salary > 2000
Nachteile:
-
Abfragen über mehrere Typen / Basisklasse langsam / schnell sehr komplex
Select e from Employee where salary > 2000
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:
Select i from Intern i where salary > 2000
Nachteile:
-
Abfragen über mehrere Typen / Basisklasse langsam / schnell sehr komplex
Select e from Employee where salary > 2000
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.2. 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:
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