| Java, Eclipse and Web programming Tutorials |
Version 0.9
Copyright © 2008 - 2009 Lars Vogel
13.10.2009
| Revision History | ||
|---|---|---|
| Revision 0.1 | 03.02.2008 | Lars Vogel |
| OpenJPA | ||
| Revision 0.2 | 29.09.2008 | Lars Vogel |
| Added EclipseLink | ||
| Revision 0.3 | 16.10.2008 | Lars Vogel |
| Bug fixes | ||
| Revision 0.4 | 08.01.2009 | Lars Vogel |
| Reworked and cleaned up the article | ||
| Revision 0.5 | 21.01.2009 | Lars Vogel |
| Moved OpenJPA to another articles | ||
| Revision 0.6 | 22.01.2009 | Lars Vogel |
| Used JUnit for the example | ||
| Revision 0.7 | 03.02.2009 | Lars Vogel |
| Changed ID generation strategy to Table | ||
| Revision 0.8 | 30.05.2009 | Lars Vogel |
| Minor re-work | ||
| Revision 0.9 | 14.10.2009 | Lars Vogel |
| Improved JPA overview chapter, described persistence.xml parameters | ||
Table of Contents
The Java Persistence API (JPA) is a specification for persisting Java objects into a relational database. The Java Persistence API let the developer easily map, store, update and retrieve data from relational database tables into Java Objects and vice versa.
The Java Persistence API simplifies the life of a developer to a great degree. It permits the developer to work with objects rather then with SQL statements.
JPA can be used in Java-EE and Java-SE applications.
This process of mapping POJO's (Plain old Java Objects) to database tables and vice versa is called Object-relational mapping (ORM). This mapping is defined via persistence metadata, which defines the mapping to the database and the relationship between the objects.
This metadata can be specified using Java annotations or XML mapping files or a combination of both. If available the XML configuration overwrites the annotations in the Java code.
JPA also defines a powerful SQL-like Query language for static and dynamic queries.
JPA is a specification and for using JPA you always need a JPA provider.
The JPA provider will use the persistence metadata information to perform the correct database operations.
Most JPA persistence provider offer the option to create automatically the database schema based on the metadata.
The persistence provider is the JPA implementation. The usage of JPA requires that an JPA provider implementation is added to the classpath.
<provider>org.eclipse.persistence.jpa.PersistenceProvider </provider>
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 describes the usage of JPA with annotations for describing the persistence metadata. See the appendix for an example using XML.
A persistable class must be annotated with javax.persistence.Entity. Entity classes will become a table in a relational database. The instances of the class will be a row in the table. The Java Persistence API implementation will create a table for the entity in your relational database. By default, the table name corresponds to the class name. You can change this with the addition to the annotation @Table(name="NEWTABLENAME")
All entities must have a primary key. Keys can be a single field or a combination of fields.
JPA allows also to auto generate the primary key in the database via the @GeneratedValue annotation.
JPA can use either your instance variables (fields) or the corresponding getters and setters to access the fields. If you want to use the setter and getter methods the Java class must follow the Java Bean naming conventions.
If JPA should access directly the instance variables then you annotate the variable. If you want to use the getter and setting you annotate them. You are not allowed to mix both methods.
All fields that should not be persisted must be explicitly 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 |
The JPA implementation allows to generate the id based on different strategies (GenerationType.TABLE, GenerationType.AUTO, etc.) I personally believe that the table based approach is preferable. The example later will demonstrate this.
Relationships between classes can be expressed. 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.
For example a person may have several jobs. The same job may be done by several people, e.g. a person p may work as a programmer and a song writer while several other people may work as programmers.
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 provides the persistence operations, e.g. to persists objects into the database. A Java object which is connected with an Entity Manager becomes an Entity.
The class "javax.persistence.EntityManager" provides this functionality.
In your application you use a factory to create the entity manager and use this entity manager to handle the database connection.
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.
In this file the database driver, the database location, the user and the password is specified.
The following will show a small example with 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.
See How to add jars 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.
Please note that I maintain this website in my private time. If you like the information I'm providing please help me by donating.For questions and discussion around this article please use the www.vogella.de Google Group. Also if you note an error in this article please post the error and if possible the correction to the Group.
I believe the following is a very good guideline for asking questions in general and also for the Google group How To Ask Questions The Smart Way.
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