JPA Hobbies Example

Application Description

This is an application with only two tables, which shows how easy it is to persist data and relationships to a database using JPA.

In this application, a Person who has a collection of Hobbies. We will store information about the Person and his hobbies in a database, and be able to search for people or hobbies in the database.

The conceptual structure of the application is:

Person and Hobby are entity classes, so they should each have an id attribute to uniquely identify each object. The id attribute is usually not shown in UML (since it doesn't have any conceptual significance).

Person has a collection of hobbies (a List, Collection, or Set) and each Hobby belongs to one Person (no sharing in this implementation). To make it easy to persist the relationship, we add a person attribute to the Hobby class. This isn't required, but makes it easy to search hobbies.

We want to be able to write code like this:

Person ts = new Person("Taksin Shinawat");

ts.addHobby( new Hobby("making money") );

ts.addHobby( new Hobby("investing") );

// Save Taksin *and* his Hobbies to database

EntityManager em = factory.createEntityManager();

em.save( taksin );

// Now find a Person in the database:

Person p = findPersonByName("Taksin Shinawat"); // create from database

// We want objects to be unique:

if (p == taksin) print("Found exactly the same object!");

// You can also find all people in database having some hobby

List<Person> people = findPersonByHobby("investing");

The attached source code shows how to do this using the Java Persistence API (JPA).

The sample code uses the Derby database. Derby is written in Java and the database can be run inside your app, so you don't need a separate database manager.

The file that configures the database is src/META-INF/persistence.xml. By editing this file you can change to another database (like MySQL), change the database name, or change parameters.

How to Create Database Schema

Usually, you would use SQL commands or a database designer tool to create a database structure (called a schema) before accessing it in Java. You'd also create a database user and password that has permission to access the database, update and add records, and nothing else! Never use the MySQL "root" or "admin" user for this purpose.

In this lab, we will let EclipseLink (JPA) create the database and schema for us! This is so we can focus on JPA and not on SQL.

Required Software

1. EclipseLink 2.x (eclipselink.jar and javax_persistence_2.0.x.vxxxxxx.jar)

2. Derby database (derby.jar) and/or HSQLDB (hsqldb.jar). These are open-source embeddable databases.

3. (Optional) Eclipse JPA Tools (the "Dali" tools) and Database Development tools plugins, or for Netbeans the Database and JPA plugins.

Create a Project and Add Jars

Create a new project named "jpademo".

Eclipse: if you have the Dali tools (an Eclipse extension), create a new JPA project. Otherwise, create a plain Java project.

Netbeans: Create a new Java project. You can then right click on the project icon and choose New -> Persistence Unit to add EclipseLink and the JPA persistence files.

You can use JPA in any Java Project

You don't need to create a JPA project in the IDE, and you are not required to have the IDE's JPA plug-in. You can use JPA in any java project. The plugins simply make it easier to manage the required persistence.xml file. You can create the META-INF directory and the required xml files yourself. Better: copy them from another project.

You doneed a JPAProvider such as EclipseLink and a JDBC driver.

Project Structure for a JPA Project

To use JPA your project should have the following structure:

jpademo/ the project base directory

src/source directory

META-INF/meta-information directory

persistence.xmlJPA configuration file

hobbies / base package for our source

model/package for domain classes

Person.javaentity classes

Hobby.java

Referenced Libraries

eclipselink.jarJPA 2.x provider (EclipseLink)

javax.persistence_2.0.3xxx.jarJPA 2.0 API

derby.jarJDBC connector for Derby database

orhsqldb.jarJDBC connector for HSQLDB

In this application we have a Person who has a collection of hobbies. We want to store information about the Person and his Hobbies to a database, and be able to search for people or hobbies in the database.

persistence.xml

This file defines persistence-related attributes of your project. If you create a JPA project (Eclipse) or add a Persistence Unit (NetBeans), the IDE will generate much of this code for you. Otherwise, you can type it in or download from my home page.

<persistence version="2.0"

xmlns="

xmlns:xsi="

xsi:schemaLocation="

<persistence-unit name="hobbies" transaction-type="RESOURCE_LOCAL">

<!-- we are using EclipseLink as the implementation of JPA -->

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

<!-- save objects from these Java classes (entities) -->

<class>hobbies.model.Person</class>

<class>hobbies.model.Hobby</class>

<!-- describe the database for JPA -->

<properties>

<property name="javax.persistence.jdbc.url"

value="jdbc:derby:/temp/hobbies;create=true"/>

<property name="javax.persistence.jdbc.user" value=""/>

<property name="javax.persistence.jdbc.password" value=""/>

<!-- JDBC driver for your database -->

<property name="javax.persistence.jdbc.driver"

value="org.apache.derby.jdbc.EmbeddedDriver"/>

<!-- this line tells Eclipselink to create schema -->

<property name="eclipselink.ddl-generation" value="create-tables"/>

</properties>

</persistence-unit>

</persistence>

Hobby Class

First, create a Person class as an entity. If you are skilled at using Eclipse, use New... -> JPA Entity class otherwise just create a plain Java class. Name it Person in package hobbies.domain.

package hobbies.domain;

import javax.persistence.*;

/** Entity implementation for Person objects.*/

@Entity

@Table(name="people")

public class Person {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

private Integer id;

private String name;

private String password;

...

// TODO constructors. At least a default constructor

// TODO get/set methods for all attributes (OK to omit setId)

// TODO toString to display person info

// TODO equals method compares persons by id or other

// distinct attribute

}

Hobby Class

package hobbies.domain;

import javax.persistence.*;

@Entity

public class Hobby implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

private Integer id;

private String name;

/** the Person for this hobby. So we can relate Person to Hobby. */

@ManyToOne

@JoinColumn(name="person_id")

private Person person;

/** initialize an unnamed hobby. */

public Hobby() { this(""); }

public Hobby(String name) { this.name = name; }

//TODO Get/Set methods for all attributes

//TODO toString returns hobby name

Add Hobbies to Person

Person needs a collection of hobbies.

He also needs a setHobbies( ) method to set the collection (used by JPA) and addHobby( ) to add one hobby (used by our application).

@OneToMany(mappedBy="person",cascade=CascadeType.ALL)

private List<Hobby> hobbies;

public List<Hobby> getHobbies() {

return hobbies;

}

public void setHobbies(List<Hobby> hobbies) {

this.hobbies = hobbies;

}

// Person.hobbies and Hobby.person must be consistent,

// so the addHobby method should set Person in Hobby:

public void addHobby(Hobby hobby) {

hobby.setPerson(this);

hobbies.add(hobby);

}

How To Persist a Unidirectional Association

What if we don't want to have a person attribute in Hobby? Person knows his hobbies, but a hobby does not know its person. In that case, in the Person class use @JoinColumn on hobbies:

public class Person {

@OneToMany(cascade=CascadeType.ALL)

@JoinColumn(name="person_id") // column name in Hobby table

private List<Hobby> hobbies;

EntityManager

EntityManager is responsible for saving, creating, and querying entities. You create an EntityManger using a factory method. You should create them only once and save emf and em for reuse.

private static final String PERSISTENCE_UNIT = "hobbies";

EntityManagerFactory emf =

Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);

EntityManager em = emf.createEntityManager();

EntityTransaction tx = em.getTransaction();

tx.begin();

em.persist( person );

tx.commit();

How to Save an Object

To save a new (unsaved) entity to the database use:

em.persist( person );

How to Retrieve an Object

This is more complex, since it depends on how you want to find the object.

To find an object by its primary key (id) is easy.

Person person = em.find( Person.class, id);

To find by name, use a JPA Query:

Query query = em.createQuery(

"SELECT p from Person p where c.name = :name");

query.setParameter("name", name);

List<Person> persons = query.getResultSet();

First Time You Run the Program

JPA will automatically create the tables in the database. To enable this, do the following.

1. In META-INF/persistence.xml set these options (values in bold):

<property name="javax.persistence.jdbc.url"

value="jdbc:derby:/temp/friends;create=true"/>

<property name="eclipselink.ddl-generation" value="create-tables"/>

2. In Main.java: the main method should add some people to the database:

public static void main( ... ) {

createPeople( );

After the First Time You Run the Program

You don't want to create tables or add schema, so edit the configuration and disable database and schema creation.

1. In META-INF/persistence.xml set these options (values in bold):

<property name="javax.persistence.jdbc.url"

value="jdbc:derby:/temp/friends;create=false"/>

<property name="eclipselink.ddl-generation" value="none"/>

The program will work even if you don't set these options, but you get error messages on the console.

2. In Main.java, comment out the call to createPeople():

public static void main( ... ) {

// createPeople( );

Error Connecting to Derby Database

Derby allows only one connection to a database. If you have an open connection, you will get an error when the program tries to connect to the database. You can have an open connection if (a) you run the program in an IDE and didn't stop the previous run, (b) you use the Database Explorer view in the IDE and didn't close the connection to database.

The other source of error is if the database location is invalid. This is rare (Derby will create directories), but check that the location is valid in persistence.xml and change it if necessary.

<property name="javax.persistence.jdbc.url"

value="jdbc:derby:/temp/hobbies;create=true"/>

How JPA Works

The Java Persistence Architecture provides an abstraction that hides the details of object persistence. An EntityManager provides services that your application uses to query, save, update, or delete persistent objects, called entities.

EntityManager is just an interface. A concrete EntityManager is provided by an implementation of JPA, called a Persistence Provider. Applications use an Entity Manager Factory to create a concrete instance of EntityManager based on configuration data. For enterprise applications, the Java EE container injects the EntityManager into your app (using annotations); your app does not create it itself.

This sequence for the World database, which contains City objects:

The Java code for the above sequence is:

final String PERSISTENCE_UNIT = "world"; // name as in persistence.xml

factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);

EntityManager em = factory.createEntityManager();

Query query = em.createQuery(

"SELECT c FROM City c WHERE c.name=:name" );

query.setParameter("name", "Bangkok");

List<City> results = query.getResultList();

for( City city: results ) System.out.println( city );

JPA Example- 1 -