Java Persistence API
Java Persistence API (JPA) je framework programovacího jazyka Java, který umožňuje objektově relační mapování (ORM). To usnadňuje práci s ukládáním objektů do databáze a naopak. Je určen jak pro Java SE, tak pro Java EE.
Entity
Entita je objekt, který reprezentuje data v databázi. Typicky entitní třída reprezentuje tabulku v relační databázi a každá instance této třídy pak koresponduje k jedné řádce tabulky.
Požadavky pro entitní třídu
- Musí být anotována anotací javax.persistence.Entity
- Musí mít public nebo protected konstruktor bez parametrů. Může ale mít i další konstruktory.
- Nesmí být deklarována jako final. To platí i pro její metody.
- Pokud session beana bude pracovat s instancemi této třídy a bude typu Remote, potom tato entitní třída musí implementovat interface Serializable
- Může dědit z entitní i ne-entitní třídy.
- Její atributy musí být deklarovány jako private, protected nebo package-private a lze k nim přistupovat pouze přes metody (gettery a settery).
Povolené typy atributů
Aby mohla být entita uložena do databáze, musí mít atributy pouze následujících typů:
- primitivní typy
- java.lang.String
- Wrapper třídy primitivních typů
- java.math.BigInteger
- java.math.BigDecimal
- java.util.Date
- java.util.Calendar
- java.sql.Date
- java.sql.Time
- java.sql.TimeStamp
- byte[]
- Byte[]
- char[]
- Character[]
- Enumerační typy
- Jiné entity a/nebo kolekce entit
- Třídy s anotací Embeddable
Multiplicity
Existují 4 typy multiplicit v entitních třídách:
- One-to-one: instance má referenci na jednu entitu jiné třídy.
- One-to-many: instance má reference na množinou instancí jiné třídy.
- Many-to-one: více instancí má referenci na jednu instanci jiné třídy.
- Many-to-many: více instancí má reference na instance jiné třídy.
Dále existují 2 typy direction:
- Unidirectional: instance má referenci na jiný objekt, ten ovšem nemá referenci zpět.
- Bidirectional: instance má referenci na jiný objekt a ten má také referenci zpět. Vidí se navzájem.
Persistence Context
Entity jsou spravovány objektem třídy EntityManager. Chceme-li ho získat v SessionBeaně, deklarovat atribut typu EntityManager a anotovat ho pomocí PersistenceContext.
Základní metody EntityManageru pro práci s objekty:
Předpokládejme, že máme definován EntityManager s názvem em a entitu s názvem entita:
- em.persist(entita): uloží objekt entita do databáze
- em.remove(entita): smaže objekt entita z databáze
- em.merge(entita): entita byla persistována, ale následně byla změněna. Po operaci merge se tyto změny projeví v databázi.
- em.find(class,id): vrátí objekt v tabulce, která koresponduje s class a má primární klíč id
Java Persistence Query Language
EntityManager umí vytvářet dotazy podobné SQL dotazům. Využívá se zde ovšem Java Persistence Query Language, což je jazyk podobný SQL a jeho použití má tu výhodu, že dotazy jsou nezávislé na zvolené technologii databáze (Oracle, Microsoft…).
Implementace JPA
Java Persistence API je pouze specifikací, nicméně není to samotná implementace, která by se dala použít. Mezi implementace JPA patří tyto produkty:
- Oracle Toplink
- Hibernate
- OpenJPA
Příklad
Máme majitele (Owner), který může mít několik aut (Car). Dále předpokládejme, že každé auto může mít vždy pouze jednoho majitele a že u auta je majitel uveden (použijeme biderectional relationship - oboustranný vztah).
Třída Owner
@Entity
public class Owner implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@OneToMany(mappedBy = "owner")
private List<Car> car;
public List<Car> getCar() {
return car;
}
public void setCar(List<Car> car) {
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Owner)) {
return false;
}
Owner other = (Owner) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "Owner[id=" + id + "]";
}
}
Třída Car
@Entity
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String color;
@ManyToOne
private Owner owner;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Owner getOwner() {
return owner;
}
public void setOwner(Owner owner) {
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Car)) {
return false;
}
Car other = (Car) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "Car[id=" + id + "]";
}
}
Session Beana
@Stateless
public class ExampleEjbBean implements ExampleEjbLocal {
@PersistenceContext
EntityManager em;
public void saveOwner(Owner owner) {
em.persist(owner);
}
public Owner getOwner(Long id) {
return (Owner) em.find(Owner.class, id);
}
}