Přeskočit na obsah

Java Persistence API

Z Wikipedie, otevřené encyklopedie

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.[1]

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.[2]

Požadavky pro entitní třídu

Aby mohly být entity persistovány, musí mít entitní třída následující vlastnosti[3]:

  • 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ů:[4]

  • 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ě, musíme deklarovat atribut typu EntityManager a anotovat ho pomocí PersistenceContext.

Transakce

Veškeré operace spojené s přístupem do databáze (operace INSERT, DELETE, UPDATE apod.) jsou prováděny uvnitř metod v Enterprise Java Beanách pomocí instance třídy EntityManager. Celá tato metoda je pak brána jako jedna ACID transakce. To mimojiné znamená, že pokud dojde k provedení celé metody bez vyhození výjimky, bude zavoláno commit a celá transakce se potvrdí. Naopak, pokud dojde k vyhození výjimky, je zavoláno rollback a co se doposud provedlo bude vráceno a databáze tak zůstane v původním konzistentím stavu.

Nicméně je také možné se starat o transakce ručně:

    @PersistenceContext
    private EntityManager em;

    public void foo(){
        EntityTransaction ut=null;
        try{
            ut=em.getTransaction();
            ut.begin();
            //do work
            ut.commit();
        } catch(Exception ex){
            ut.rollback();
        } 
    }

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 (operace INSERT)
  • em.remove(entita): smaže objekt entita z databáze (operace DELETE)
  • 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 (operace UPDATE).
  • em.find(class,id): vrátí objekt v tabulce, která koresponduje s class a má primární klíč id (operace SELECT)

Java Persistence Query Language

EntityManager umí vytvářet dotazy podobné SQL dotazům. Využívá se zde ovšem Java Persistence Query Language[5], 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…). Navíc mají objektové vlastnosti, takže v dotazech nezmiňujeme konkrétní názvy tabulek databáze či jejich vlastností, nýbrž v dotazech uvádíme přímo názvy tříd/atributů, k porovnávání můžeme využívat i referencí objektů.

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 Bean

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


Reference

Související články

Externí odkazy