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

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

Transcript of Beziehungen Persistente Domänenmodelle mit JPA 2.0 und Bean Validation.

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