Nějakou dobu jsem zanedbával certifikace. Říkal jsem si, že bych mohl konečně dotáhnout JPA a v rámci přípravy jsem narazil na pár zajímavostí. Přitom jsem si zoufal, že jsem mohl předchozí projekty napsat výrazně lépe, ale utěšuje mě myšlenka, že byste měli být nespokojení se svým kódem, který jste napsali před rokem. Konkrétně chci psát o vazebních tabulkách legacy databází, které nejsou triviální, tj. neobsahují jen klíče ale i nějaká metadata.

Databáze

Je dána databáze, nad kterou stavíte aplikaci s JPA.

Naivní řešení

Jako první vás jistě napadne řešení, které jsem sám doposud používal, vytvořit entitu EmployeeProject. Funguje to, ale předpokládejme že nejčastěji vás bude zajímat přistup z entity Employee na Project, ale přitom musíte jít přes entity EmployeeProject.

Elegantní řešení

Existuje ovšem elegantnější řešení, jak se z Employee dostat přímo k entitám Project a zároveň mít případně k dispozici i metadata v embeddable objektu ProjectAllocation. (V anglické terminologii Relationship State či Association class)

import javax.persistence.*;
import java.util.Map;
@Entity
public class Employee {
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
@Column(name = "EMP_ID")
private Integer id;
@Column(name = "EMP_NAME")
private String name;
@ElementCollection
@CollectionTable(name = "EMP_PROJECTS", joinColumns = @JoinColumn(name = "EMP_ID"))
@MapKeyJoinColumn(name = "PROJECT_ID")
private Map<Project, ProjectAllocation> currentProjects;
}
view raw Employee.java hosted with ❤ by GitHub
CREATE TABLE EMPLOYEE (
EMP_ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
EMP_NAME VARCHAR (100) NOT NULL,
PRIMARY KEY (EMP_ID)
);
CREATE TABLE PROJECT (
PROJECT_ID INT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
PRIMARY KEY (PROJECT_ID)
);
CREATE TABLE EMP_PROJECTS (
EMP_ID INT NOT NULL,
PROJECT_ID INT NOT NULL,
PROJECT_COMMENT VARCHAR (200) NOT NULL,
PROJECT_START_DATE DATE NOT NULL,
PROJECT_END_DATE DATE NOT NULL,
PROJECT_HOURS_PER_WEEK DOUBLE NOT NULL,
PRIMARY KEY (EMP_ID, PROJECT_ID)
);
view raw init-h2.sql hosted with ❤ by GitHub
import javax.persistence.*;
@Entity
public class Project {
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
@Column(name = "PROJECT_ID")
private Integer id;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Project)) {
return false;
}
Project project = (Project) o;
if (id != null ? !id.equals(project.id) : project.id != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
view raw Project.java hosted with ❤ by GitHub
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;
@Embeddable
public class ProjectAllocation {
@Temporal(TemporalType.DATE)
@Column(name = "PROJECT_START_DATE")
private Date startDate;
@Temporal(TemporalType.DATE)
@Column(name = "PROJECT_END_DATE")
private Date endDate;
@Column(name = "PROJECT_COMMENT")
private String comment;
@Column(name = "PROJECT_HOURS_PER_WEEK")
private Double hoursPerWeek;
}

Související