Version 1.0
Copyright © 2008 - 2010 Lars Vogel
18.10.2010
| Revision History | ||
|---|---|---|
| Revision 0.1 | 03.02.2008 | Lars Vogel |
| Created | ||
| Revision 0.2 - 1.0 | 29.09.2008 - 18.08.2010 | Lars Vogel |
| bug fixes and enhancementss | ||
Table of Contents
The process of mapping Java Objects to database tables and vice versa is called "Object-relational mapping"" (ORM). The Java Persistence API (JPA) is one approach to ORM. Via JPA the developer can map, store, update and retrieve data from relational databases to Java Objects and vice versa, JPA permits the developer to work directly with objects rather then with SQL statements. JPA is a specification and several implementations are available. This tutorial will use EclipseLink as JPA implementation. The JPA implementation is typically called persistence provider. JPA can be used in Java-EE and Java-SE applications.
The mapping between Java objects and database tables is defined via persistence metadata, JPA defines the metadata via annotations in the Java class. Alternatively the metadata can be defined via XML or a combination of both. A XML configuration overwrites the annotations. The JPA provider will use the persistence metadata information to perform the correct database operations.
JPA also defines a SQL-like Query language for static and dynamic queries.
Most JPA persistence provider offer the option to create automatically the database schema based on the metadata.
The following description will be based on the usage of annotations.
To allow that a class can be persisted it must be annotated with "javax.persistence.Entity"". JPA will create a table for the entity in your relational database, e.g. every entity class will be mapped to at least one table. Instances of the class will be a row in the table.
All entities must have a primary key. Keys can be a single field or a combination of fields. JPA allows to auto-generate the primary key in the database via the @GeneratedValue annotation.
By default, the table name corresponds to the class name. You can change this with the addition to the annotation @Table(name="NEWTABLENAME").
The fields of the Entity will be saved in the database. JPA can use either your instance variables (fields) or the corresponding getters and setters to access the fields. You are not allowed to mix both methods. If you want to use the setter and getter methods the Java class must follow the Java Bean naming conventions. JPA persists per default all fields of an Entity, if fields should not be saved they must be marked with @Transient.
By default each field is mapped to a column with the name of the field. You can change the default name via @Column(name="newColumnName").
The following annotations can be used.
Table 1. Annotations for fields / getter and setter
| @Id | Identifies the unique ID of the database entry |
| @GeneratedValue | Together with ID defines that this value is generated automatically. |
| @Transient | Field will not be saved in database |
JPA allows to define relationships between classes, e.g. it can be defined that a class is part of another class (containment). Classes can have one to one, one to many, many to one, and many to many relationships with other classes.
A relationship can be bidirectional or unidirectional, e.g. in a bidirectional relationship both classes store a reference to each other while in an unidirectional case only one class has a reference to the other class. Within a bidirectional relationship you need to specify the owning side of this relationship in the other class with the attribute "mappedBy", e.g. @ManyToMany(mappedBy="attributeOfTheOwningClass".
The entity manager "javax.persistence.EntityManager" provides the operations from and to the database, e.g. find objects, persists them, remove objects from the database, etc. In a JavaEE application the entity manager is automatically inserted in the web application. Outside JavaEE you need to manage the entity manager yourself.
Entities which are managed by an Entity Manager will automatically propagate these changes to the database (if this happens within a commit statement). If the Entity Manager is closed (via close()) then the managed entities are in a detached state. If synchronize them again with the database a Entity Manager provides the merge() method.
The persistence context describes all Entities of one Entity manager.
The EntityManager is created by the EntitiyManagerFactory which is configured by the persistence unit. A set of entities which are logical connected will be grouped via a persistence unit. These persistence units are described via the file "persistence.xml" in the directory META-INF in the source folder. "persistence.xml" defines the connection data to the database, e.g. the driver, the user and the password,
Download the EclipseLink implementation from http://www.eclipse.org/eclipselink/downloads/ . The download contains several jars. We need the following jars:
eclipselink.jar
persistence.jar
The example later will be using Derby as a database. Download Derby from http://db.apache.org/derby/ From this tutorial we will need the "derby.jar".
For a tutorial on Derby (which is not required for this tutorial) please see Apache Derby .
The following will demonstrate the usage of JPA.
Create a Java project "de.vogella.jpa.eclipselink". Create a folder lib, place the required JPA jars into this folder and add them to the classpath as described earlier. Also add the derby.jar to the folder lib and add it to the classpath .
Create a package "de.vogella.jpa.eclipselink.model" and create the following classes.
package de.vogella.jpa.eclipselink.model;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
@Entity
public class Family {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String description;
@OneToMany(mappedBy = "family")
private final List<Person> members = new ArrayList<Person>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Person> getMembers() {
return members;
}
}
package de.vogella.jpa.eclipselink.model;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private String id;
private String firstName;
private String lastName;
private Family family;
private String nonsenseField = "";
private List<Job> jobList = new ArrayList<Job>();
public String getId() {
return id;
}
public void setId(String Id) {
this.id = Id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
// Leave the standard column name of the table
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@ManyToOne
public Family getFamily() {
return family;
}
public void setFamily(Family family) {
this.family = family;
}
@Transient
public String getNonsenseField() {
return nonsenseField;
}
public void setNonsenseField(String nonsenseField) {
this.nonsenseField = nonsenseField;
}
@OneToMany
public List<Job> getJobList() {
return this.jobList;
}
public void setJobList(List<Job> nickName) {
this.jobList = nickName;
}
}
package de.vogella.jpa.eclipselink.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Job {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private double salery;
private String jobDescr;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getSalery() {
return salery;
}
public void setSalery(double salery) {
this.salery = salery;
}
public String getJobDescr() {
return jobDescr;
}
public void setJobDescr(String jobDescr) {
this.jobDescr = jobDescr;
}
}
Create a directory META-INF in your src folder and create the following file persistence.xml.
Each JPA provider is free to define its own parameter values for the database connection, user, password, etc. Some the parameters in this examples are therefore EclipseLink specific.
Please adjust the path of your database. This database will be created automatically.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="people"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <class>de.vogella.jpa.eclipselink.model.Family</class> <class>de.vogella.jpa.eclipselink.model.Person</class> <class>de.vogella.jpa.eclipselink.model.Job</class> <properties> <property name="eclipselink.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" /> <property name="eclipselink.jdbc.url" value="jdbc:derby:C:/DerbyDatabases/hellojpa-database8;create=true" /> <!-- I work in this example without user / password.--> <property name="eclipselink.jdbc.user" value="" /> <property name="eclipselink.jdbc.password" value="" /> <!-- EclipseLink should create the database schema automatically --> <property name="eclipselink.ddl-generation" value="drop-and-create-tables" /> <property name="eclipselink.ddl-generation.output-mode" value="database" /> </properties> </persistence-unit> </persistence>
The following is implemented as a Junit Test. See JUnit Introduction. for more information on JUnit testing.
The setup() method will create a few test entries. After the test entries are created, they will be read and the one field of the entries is changed and saved to the database.
package de.vogella.jpa.eclipselink.main;
import static org.junit.Assert.assertTrue;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import org.junit.Before;
import org.junit.Test;
import de.vogella.jpa.eclipselink.model.Family;
import de.vogella.jpa.eclipselink.model.Person;
public class JpaTest {
private static final String PERSISTENCE_UNIT_NAME = "people";
private EntityManagerFactory factory;
@Before
public void setUp() throws Exception {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
// Begin a new local transaction so that we can persist a new entity
em.getTransaction().begin();
// Read the existing entries
Query q = em.createQuery("select m from Person m");
// Persons should be empty
// Do we have entries?
boolean createNewEntries = (q.getResultList().size() == 0);
// No, so lets create new entries
if (createNewEntries) {
assertTrue(q.getResultList().size() == 0);
Family family = new Family();
family.setDescription("Family for the Knopfs");
em.persist(family);
for (int i = 0; i < 40; i++) {
Person person = new Person();
person.setFirstName("Jim_" + i);
person.setLastName("Knopf_" + i);
em.persist(person);
// First we have to persists the job
// Now persists the new person
family.getMembers().add(person);
em.persist(person);
em.persist(family);
}
}
// Commit the transaction, which will cause the entity to
// be stored in the database
em.getTransaction().commit();
// It is always good practice to close the EntityManager so that
// resources are conserved.
em.close();
}
@Test
public void checkAvailablePeople() {
// Now lets check the database and see if the created entries are there
// Create a fresh, new EntityManager
EntityManager em = factory.createEntityManager();
// Perform a simple query for all the Message entities
Query q = em.createQuery("select m from Person m");
// We should have 40 Persons in the database
assertTrue(q.getResultList().size() == 40);
em.close();
}
@Test
public void checkFamily() {
EntityManager em = factory.createEntityManager();
// Go through each of the entities and print out each of their
// messages, as well as the date on which it was created
Query q = em.createQuery("select f from Family f");
// We should have one family with 40 persons
assertTrue(q.getResultList().size() == 1);
assertTrue(((Family) q.getSingleResult()).getMembers().size() == 40);
em.close();
}
@Test(expected = javax.persistence.NoResultException.class)
public void deletePerson() {
EntityManager em = factory.createEntityManager();
// Begin a new local transaction so that we can persist a new entity
em.getTransaction().begin();
Query q = em
.createQuery("SELECT p FROM Person p WHERE p.firstName = :firstName AND p.lastName = :lastName");
q.setParameter("firstName", "Jim_1");
q.setParameter("lastName", "Knopf_!");
Person user = (Person) q.getSingleResult();
em.remove(user);
em.getTransaction().commit();
Person person = (Person) q.getSingleResult();
// Begin a new local transaction so that we can persist a new entity
em.close();
}
}
The project should now look like the following.

You should be able to run the JUnit tests sucessfully .
Thank you for practicing with this tutorial.
I maintain this tutorial in my private time. If you like the information please help me by using flattr or donating or by
|
Before posting questions, please see the vogella FAQ . If you have questions or find an error in this article please use the www.vogella.de Google Group . I have created a short list how to create good questions which might also help you. .
Alternative to the annotations you can also use XML metadata to define the persistence metadata.
You can create a file orm.xml within the META-INF directory to describes this data.
The following file describes the persistence data in an external file orm.xml. No annotations are required in Person.java. We use for illustration a simplified example of person.
package datamodel;
import javax.persistence.*;
@Entity
@Table(name="MYAPPLICATION.PEOPLETABLE")
public class Person implements IPerson {
private String id;
private String firstName;
private String lastName;
private String nonsenseField="";
@Id
public String getId() {
return id;
}
public void setId(String Id) {
this.id = Id;
}
// Name the column to "MYFIRST"
@Column(name="MYFIRST")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
// Leave the standard column name of the table
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Transient
public String getNonsenseField() {
return nonsenseField;
}
public void setNonsenseField(String nonsenseField) {
this.nonsenseField = nonsenseField;
}
}
This is required orm.xml for this mapping.
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd" version="1.0">
<package>
datamodel
</package>
<access>
PROPERTY
</access>
<entity class="Person">
<table schema="MYAPPLICATION" name="PEOPLETABLE"/>
<attributes>
<id name="id">
<column name="ID"/>
</id>
<basic name="lastName">
<column name="LASTNAME"/>
</basic>
<basic name="firstName">
<column name="MYFIRST"/>
</basic>
<transient name="nonsenseField" />
</attributes>
</entity>
</entity-mappings>
http://www.eclipse.org/eclipselink/ EclipseLink
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/persistenceapi/ Using the Java Persistence API in Desktop Applications
http://www.ibm.com/developerworks/java/library/j-typesafejpa/index.html Dynamic, typesafe queries in JPA 2.0 using the Criteria API