API trajnosti Java (JPA) Java je specifikacija koja premošćuje jaz između relacijskih baza podataka i objektno orijentiranog programiranja. Ovaj dvodijelni vodič predstavlja JPA i objašnjava kako su Java objekti modelirani kao JPA entiteti, kako su definirani odnosi entiteta i kako koristiti JPA EntityManager
s uzorkom Repozitorija u vašim Java aplikacijama.
Imajte na umu da ovaj vodič koristi Hibernate kao JPA pružatelja usluga. Većina koncepata može se proširiti na druge okvire trajnosti Java.
Što je JPA?
Pogledajte "Što je JPA? Uvod u Java Persistent API" da biste saznali više o razvoju JPA i srodnih okvira, uključujući EJB 3.0. i JDBC.
Predmetni odnosi u JPA
Relacijske baze podataka postoje kao sredstvo za pohranu programskih podataka od 1970-ih. Iako programeri danas imaju mnogo alternativa relacijskoj bazi podataka, ova vrsta baze podataka je skalabilna i dobro razumljiva te se i dalje široko koristi u razvoju malih i velikih softvera.
Java objekti u kontekstu relacijske baze podataka definirani su kao entiteti . Entiteti se postavljaju u tablice gdje zauzimaju stupce i retke. Programeri koriste strane ključeve i spajaju tablice kako bi definirali odnose između entiteta - naime jedan-na-jedan, jedan-na-mnogo i odnosi mnogo-na-mnogo. Također možemo koristiti SQL (jezik strukturiranih upita) za dohvaćanje podataka i interakciju s njima u pojedinačnim tablicama i više njih, koristeći ograničenja stranog ključa. Relacijski model je ravan, ali programeri mogu pisati upite za dohvaćanje podataka i konstruiranje objekata iz tih podataka.
Neusklađenost impedancije objektnih odnosa
Možda vam je poznat pojam neusklađenosti impedancije relacija objekt , koji se odnosi na izazov mapiranja podatkovnih objekata u relacijsku bazu podataka. Do ove neusklađenosti dolazi zato što objektno orijentirani dizajn nije ograničen na odnose jedan-na-jedan, jedan-na-mnogo i mnogo-na-mnogo. Umjesto toga, u objektno orijentiranom dizajnu razmišljamo o objektima, njihovim atributima i ponašanju te o tome kako se objekti povezuju. Dva su primjera inkapsulacija i nasljeđivanje:
- Ako objekt sadrži drugi objekt, to definiramo enkapsulacijom - odnos ima-ima .
- Ako je objekt specijalizacija drugog objekta, to definiramo nasljeđivanjem - to je- odnos.
Asocijacija, agregacija, sastav, apstrakcija, generalizacija, realizacija i ovisnosti sve su to objektno orijentirani koncepti programiranja koje može biti izazov mapirati u relacijski model.
ORM: Objektno-relacijsko mapiranje
Neusklađenost između objektno orijentiranog dizajna i relacijskog modeliranja baze podataka dovela je do klase alata razvijenih posebno za objektno-relacijsko mapiranje (ORM). ORM alati poput Hibernate, EclipseLink i iBatis prevode modele relacijskih baza podataka, uključujući entitete i njihove odnose, u objektno orijentirane modele. Mnogi od ovih alata postojali su prije JPA specifikacije, ali bez standarda njihove su značajke ovisile o dobavljaču.
Prvi put objavljen kao dio EJB 3.0 2006. godine, Java Persistent API (JPA) nudi standardni način označavanja objekata tako da se mogu mapirati i pohraniti u relacijsku bazu podataka. Specifikacija također definira zajednički konstrukt za interakciju s bazama podataka. Imati ORM standard za Javu donosi dosljednost implementacijama dobavljača, a istovremeno omogućava fleksibilnost i dodatke. Kao primjer, dok je izvorna JPA specifikacija primjenjiva na relacijske baze podataka, neke implementacije dobavljača proširile su JPA za upotrebu s NoSQL bazama podataka.
Evolucija JPA
Prvo izdanje JPA, verzija 1.0, objavljeno je 2006. putem Java Community Process (JCP) kao Zahtjev za specifikaciju Java (JSR) 220. Verzija 2.0 (JSR 317) objavljena je 2009., verzija 2.1 (JSR 338) 2013. godine, a verzija 2.2 (izdanje za održavanje JSR 338) objavljena je 2017. JPA 2.2 odabrana je za uključivanje i stalni razvoj u Jakarta EE.
Početak rada sa JPA
API za trajnost Java specifikacija je, a ne implementacija: definira uobičajenu apstrakciju koju možete koristiti u svom kodu za interakciju s ORM proizvodima. Ovaj odjeljak daje pregled nekih važnih dijelova JPA specifikacije.
Naučit ćete kako:
- U bazi podataka definirajte entitete, polja i primarne ključeve.
- Stvorite odnose između entiteta u bazi podataka.
- Radite s
EntityManager
i njegovim metodama.
Definiranje entiteta
Da biste definirali entitet, morate stvoriti klasu koja je označena @Entity
bilješkom. @Entity
Primjedba je marker komentarima , koji se koristi za otkrivanje trajne entitete. Na primjer, ako želite stvoriti entitet knjige, dodali biste ga na sljedeći način:
@Entity public class Book { ... }
Prema zadanim postavkama, ovaj će se entitet preslikati u Book
tablicu, kako je određeno danim nazivom klase. Ako ste ovaj entitet željeli preslikati u drugu tablicu (i, po želji, u određenu shemu), za to biste mogli upotrijebiti @Table
bilješku. Evo kako biste mapirali Book
razred u tablicu BOOKS:
@Entity @Table(name="BOOKS") public class Book { ... }
Ako je tablica KNJIGE bila u shemi PUBLISHING, shemu možete dodati u @Table
bilješku:
@Table(name="BOOKS", schema="PUBLISHING")
Mapiranje polja u stupce
S entitetom mapiranim u tablicu, sljedeći je zadatak definirati njegova polja. Polja su definirana kao varijable člana u klasi, s tim da se ime svakog polja preslikava u naziv stupca u tablici. Možete nadjačati ovo zadano mapiranje pomoću @Column
bilješke, kao što je prikazano ovdje:
@Entity @Table(name="BOOKS") public class Book { private String name; @Column(name="ISBN_NUMBER") private String isbn; ... }
U ovom smo primjeru prihvatili zadano mapiranje za name
atribut, ali naveli smo prilagođeno mapiranje za isbn
atribut. name
Atribut će se mapirati na ime stupca, ali isbn
atribut će se mapirati na ISBN_NUMBER kolonu.
@Column
Komentarima nam omogućava definiranje dodatnih svojstava polju / stupcu, uključujući i dužine, da li je nulte, da li to mora biti jedinstven, njegova preciznost i skale (ako je decimalna vrijednost), da li se može umetnuti i updatable, i tako dalje .
Određivanje primarnog ključa
Jedan od zahtjeva za relacijskom tablicom baze podataka jest da mora sadržavati primarni ključ ili ključ koji jedinstveno identificira određeni redak u bazi podataka. U JPA, napomenom koristimo @Id
polje za primarni ključ tablice. Primarni ključ mora biti Java primitivni tip, primitivni omot, kao što je Integer
ili Long
, a String
, a Date
, a BigInteger
ili a BigDecimal
.
In this example, we map the id
attribute, which is an Integer
, to the ID column in the BOOKS table:
@Entity @Table(name="BOOKS") public class Book { @Id private Integer id; private String name; @Column(name="ISBN_NUMBER") private String isbn; ... }
It is also possible to combine the @Id
annotation with the @Column
annotation to overwrite the primary key's column-name mapping.
Relationships between entities
Now that you know how to define an entity, let's look at how to create relationships between entities. JPA defines four annotations for defining entities:
@OneToOne
@OneToMany
@ManyToOne
@ManyToMany
One-to-one relationships
The @OneToOne
annotation is used to define a one-to-one relationship between two entities. For example, you may have a User
entity that contains a user's name, email, and password, but you may want to maintain additional information about a user (such as age, gender, and favorite color) in a separate UserProfile
entity. The @OneToOne
annotation facilitates breaking down your data and entities this way.
The User
class below has a single UserProfile
instance. The UserProfile
maps to a single User
instance.
@Entity public class User { @Id private Integer id; private String email; private String name; private String password; @OneToOne(mappedBy="user") private UserProfile profile; ... }
@Entity public class UserProfile { @Id private Integer id; private int age; private String gender; private String favoriteColor; @OneToOne private User user; ... }
The JPA provider uses UserProfile
's user
field to map UserProfile
to User
. The mapping is specified in the mappedBy
attribute in the @OneToOne
annotation.
One-to-many and many-to-one relationships
The @OneToMany
and @ManyToOne
annotations facilitate both sides of the same relationship. Consider an example where a Book
can have only one Author
, but an Author
may have many books. The Book
entity would define a @ManyToOne
relationship with Author
and the Author
entity would define a @OneToMany
relationship with Book
.
@Entity public class Book { @Id private Integer id; private String name; @ManyToOne @JoinColumn(name="AUTHOR_ID") private Author author; ... }
@Entity public class Author { @Id @GeneratedValue private Integer id; private String name; @OneToMany(mappedBy = "author") private List books = new ArrayList(); ... }
In this case, the Author
class maintains a list of all of the books written by that author and the Book
class maintains a reference to its single author. Additionally, the @JoinColumn
specifies the name of the column in the Book
table to store the ID of the Author
.
Many-to-many relationships
Finally, the @ManyToMany
annotation facilitates a many-to-many relationship between entities. Here's a case where a Book
entity has multiple Author
s:
@Entity public class Book { @Id private Integer id; private String name; @ManyToMany @JoinTable(name="BOOK_AUTHORS", [email protected](name="BOOK_ID"), [email protected](name="AUTHOR_ID")) private Set authors = new HashSet(); ... }
@Entity public class Author { @Id @GeneratedValue private Integer id; private String name; @ManyToMany(mappedBy = "author") private Set books = new HashSet(); ... }
In this example, we create a new table, BOOK_AUTHORS
, with two columns: BOOK_ID
and AUTHOR_ID
. Using the joinColumns
and inverseJoinColumns
attributes tells your JPA framework how to map these classes in a many-to-many relationship. The @ManyToMany
annotation in the Author
class references the field in the Book
class that manages the relationship; namely the authors
property.
That's a quick demo for a fairly complex topic. We'll dive further into the @JoinTable
and @JoinColumn
annotations in the next article.
Working with the EntityManager
EntityManager
is the class that performs database interactions in JPA. It is initialized through a configuration file named persistence.xml
. This file is found in the META-INF
folder in your CLASSPATH
, which is typically packaged in your JAR or WAR file. The persistence.xml
file contains:
- The named "persistence unit," which specifies the persistence framework you're using, such as Hibernate or EclipseLink.
- A collection of properties specifying how to connect to your database, as well as any customizations in the persistence framework.
- A list of entity classes in your project.
Let's look at an example.
Configuring the EntityManager
First, we create an EntityManager
using the EntityManagerFactory
retrieved from the Persistence
class:
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Books"); EntityManager entityManager = entityManagerFactory.createEntityManager();
In this case we've created an EntityManager
that is connected to the "Books" persistence unit, which we've configured in the persistence.xml
file.
The EntityManager
class defines how our software will interact with the database through JPA entities. Here are some of the methods used by EntityManager
:
find
retrieves an entity by its primary key.createQuery
creates aQuery
instance that can be used to retrieve entities from the database.createNamedQuery
loads aQuery
that has been defined in a@NamedQuery
annotation inside one of the persistence entities. Named queries provide a clean mechanism for centralizing JPA queries in the definition of the persistence class on which the query will execute.getTransaction
defines anEntityTransaction
to use in your database interactions. Just like database transactions, you will typically begin the transaction, perform your operations, and then either commit or rollback your transaction. ThegetTransaction()
method lets you access this behavior at the level of theEntityManager
, rather than the database.merge()
adds an entity to the persistence context, so that when the transaction is committed, the entity will be persisted to the database. When usingmerge()
, objects are not managed.persist
adds an entity to the persistence context, so that when the transaction is committed, the entity will be persisted to the database. When usingpersist()
, objects are managed.refresh
refreshes the state of the current entity from the database.flush
synchronizes the state of the persistence context with the database.
Ne brinite se hoćete li integrirati sve ove metode odjednom. Upoznat ćete ih radeći izravno s timom EntityManager
, što ćemo učiniti više u sljedećem odjeljku.