18
Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Embed Size (px)

Citation preview

Page 1: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Beziehungen

Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Page 2: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Eine Parent-Child Beziehung

Wie sieht das Klassen-Modell aus? Employee hat eine Referenz auf Department Department hat eine Collection von Employee

Referenzen Beides: Bidirektionale Beziehung

Unabhängig davon ist das zugrundeliegende DB-Schema:

Page 3: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Eine Parent-Child Beziehung

Mapping des Klassenmodells auf das DB-Schema mittels JPA: Metadata ist erforderlich. Je nach Klassenmodell wird entweder eine

many-to-one Beziehung oder eine one-to-many Beziehung gemappt

Falls beide Richtungen gemappt werden sollen, so muss definiert werden, dass für beide derselbe Foreign-Key zugrunde liegt.

Page 4: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Eine Parent-Child Beziehung@Entitypublic class Employee { … @ManyToOne private Department department; …

@Entitypublic class Department { … @OneToMany @JoinColumn (name=”department_id”) private Set<Employee> employees =

new HashSet<Employee>(); …

@Entitypublic class Department { … @OneToMany (mappedBy = “department“) private Set<Order> employees =

new HashSet<Employee>(); …

Mapping der one-to-many Beziehung• Field/Property muss ein Interface sein• Achtung:– Unidirektionales one-to-many ohne

Beziehungstabelle wird erst ab JPA2 unterstützt

Mapping der many-to-one Beziehung

Mapping der bidirektionalen Beziehung

• JPA muss wissen, dass nur ein Foreign-Key für beide Richtungen existiert.

Page 5: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Collection Types

• Richtung

– Unidirektional

– Bidirektional

• Kardinalität

– One-to-one

– Many-to-one

– One-to-many

– many-to-many

Employee Project

Employee Address

Source Target

Employee Department* 1

Employee Project* *

Employee Address1 1

Employee Phone1 *

Page 6: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

one-to-one, unidirektional

Employee

@OneToOneprivate Address address;

entspricht:

@OneToOne@JoinColumn(name="address_id", referencedColumnName = "id")private Address address;

(JPA2 unterstützt auch one-to-one mit einer zusätzlichen Zwischentabelle)

-id : int-name : String-salary : long

Employee

-id : int-street : String-city : String-state : String-zip : String

Address

0..1

ADDRESS

ID

CITY (O) STATE (O) STREET (O) ZIP (O)

EMPLOYEE

ID

NAME (O) SALARY (O) STARTDATE (O) DEPARTMENT_ID (O) (FK)MANAGER_ID (O) (FK)ADDRESS_ID (O) (FK)

Page 7: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

many-to-one, unidirektional

-id : int-name : String-salary : long

Employee

-id : int-name : String

Department

* 0..1

DEPARTMENT

ID

NAME (O)

EMPLOYEE

ID

NAME (O) SALARY (O) STARTDATE (O) DEPARTMENT_ID (O) (FK)MANAGER_ID (O) (FK)ADDRESS_ID (O) (FK)

DEPARTMENT

ID

NAME (O)

EMPLOYEE

ID

NAME (O) SALARY (O) STARTDATE (O) DEPARTMENT_ID (O) (FK)MANAGER_ID (O) (FK)ADDRESS_ID (O) (FK)

DEPARTMENT

ID

NAME (O)

EMPLOYEE

ID

NAME (O) SALARY (O) STARTDATE (O) DEPARTMENT_ID (O) (FK)MANAGER_ID (O) (FK)ADDRESS_ID (O) (FK)

Employee@ManyToOneprivate Department department;

(JPA2 unterstützt auch many-to-one mit einer zusätzlichen Zwischentabelle)

Page 8: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

one-to-many, bidirektional

Phone

@ManyToOne(optional = false)private Employee employee;

Employee

@OneToMany(mappedBy = "employee")private Collection<Phone> phones;

-id : int-phonenumber : String-type : String

Phone

-id : int-name : String-salary : long

Employee

* 1

EMPLOYEE

ID

NAME (O) SALARY (O) STARTDATE (O) DEPARTMENT_ID (O) (FK)MANAGER_ID (O) (FK)ADDRESS_ID (O) (FK)

PHONE

ID

PHONENUMBER (O) TYPE (O) EMPLOYEE_ID (O) (FK)

Page 9: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

many-to-many, bidirektional

Employee

@ManyToMany(mappedBy = "employees")

private Collection<Project> projects;

Project

@ManyToMany

private Collection<Employee> employees;

-id : int-name : String-salary : long

Employee

-id : int-name : String

Project

* *

PROJECT

ID

DTYPE (O) NAME (O)

PROJECT_EMPLOYEE

PROJECTS_ID (FK)EMPLOYEES_ID (FK)

EMPLOYEE

ID

NAME (O) SALARY (O) STARTDATE (O) DEPARTMENT_ID (O) (FK)MANAGER_ID (O) (FK)ADDRESS_ID (O) (FK)

Page 10: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Many-To-Many Beziehungen

„If you think that two objects share a simple many-to-many relationship, you haven't looked closely enough at the domain. There is a third object waiting to be discovered with attributes and a life cycle all its own.“ - Dierk König

• Oft sind weitere Daten auf der Zwischentabelle nötig

• Üblicherweise mappt man dann die Zwischentabelle auf eine eigene Entity

Page 11: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

one-to-many, unidirektional• Bei einer unidirektionalen one-to-many Beziehungen

fehlt das mappedBy Element und das Target hat keine Rückbeziehung

• JPA verwendet in diesen Fällen ebenfalls eine Beziehungstabelle

• JPA 2 spezifiziert die unidirektionale one-to-many Beziehung ohne Zwischentabelle.

@OneToMany@JoinColumn (name=”department_id”)private Set<Employee> employees = new HashSet<Employee>();

@OneToManyprivate Set<Employee> employees = new HashSet<Employee>();

Page 12: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Bidirektionale Beziehungen

JPA verändert die Java-Semantik nicht!

D.h. der korrekte Unterhalt von bidirektionalen Beziehungen ist Sache der Applikation!

Department taxes = new Department();

Employee john = new Employee();

taxes.getEmployees().add(john);

john.setDepartment(taxes);

Page 13: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Bidirektionale Beziehungen

Best Practice: Convenience Methoden auf den Entities:@Entitypublic class Department { … @OneToMany private List<Employee> employees = new ArrayList<Employee>();

public void addEmployee(Employee employee){ if (employee == null)

throw new IllegalArgumentException(“Null employee“); if (employee.getDepartment() != null) employee.getDepartment().getEmployees().remove(employee); getEmployees().add(employee); employee.setDepartment(this); } …}

Analog: removeEmployee() sowie Methoden auf Employee.

Page 14: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Verwendung von Collections• java.util.Set

– Eindeutig (Object.equals())– @OneToManyprivate Set<Phone> phones;

• java.util.List– geordnet, kann sortiert werden– @OneToMany@OrderBy("phonenumber ASC")private List<Phone> phones;

• java.util.Map– Key/Value Paare– @OneToMany@MapKey(name = "phonenumber")private Map<String, Phone> phones;

JPA 2:Persistenter Index@OneToMany@OrderColumn(name="index")private List<Phone> phones;

Page 15: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Lazy- und Eager-Loading

• Default bei one-to-one und many-to-one– FetchType.EAGER

• Default bei one-to-many und many-to-many– FetchType.LAZY

• Defaultverhalten kann übersteuert werden. z.B.– @OneToMany(fetch = FetchType.EAGER)private Set<Phone> phones;

EntityManager em = ...Department foreignAffairs = em.find(Department.class, 123);foreignAffairs.getEmployees().iterator().next();

Beziehungen werden transparent (nach)geladen:

Page 16: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Speichern und Löschen von BeziehungenDepartment taxes = new Department();Employee john = new Employee();taxes.addEmployee(john);Employee jane = new Employee();taxes.addEmployee(jane);

em.persist(taxes);em.persist(john);em.persist(jane);em.flush();

for (Employee empl : taxes.getEmployees()){ em.remove(empl);}em.remove(taxes);em.flush();

• Jede Entity hat einen eigenen, unabhängigen Lifecycle!• IllegalStateException wenn vergessen wird, eine assoziierte Entity zu persistieren.

• Delete all entities individually

Page 17: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Transitive Persistenz

Persistenz wird von JPA propagiert auf assoziierte Entities.

@OneToMany (mappedBy = “department“, cascade = CascadeType.ALL)

private Set<Employee> employees = new HashSet<Employee>();

Department taxes = new Department();Employee john = new Employee();taxes.addEmployee(john);Employee jane = new Employee();taxes.addEmployee(jane);

em.persist(taxes);em.flush();

em.delete(taxes);em.flush();

• Kaskadierung wird auf der Assoziation konfiguriert

• Speichern eines Parents speichert auch alle Kinder

• Löschen eines Parents löscht auch alle Kinder.

CascadeType {ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH};

Page 18: Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation

Orphan DeletionPhone phone1 = …Employee john = em.find(Employee.class, 123);john.getPhones().remove(phone1);em.flush();

• Child wird nicht gelöscht!

Entfernen eines Kindes aus der Collection des Parents setzt nur den Foreign Key auf der Kind-Tabelle auf NULL.– FK Constraint Verletzung möglich– Das Kind ist nun “orphaned”

• In JPA 1 muss das Löschen von Orphans explizit in der Applikation erfolgen.• JPA 2 unterstützt das automatische Löschen von Orphans

@OneToMany(cascade=ALL, mappedBy=”customer”, orphanRemoval=true) public Set<Order> getOrders() { return orders; }