Quick Introduction to Java Persistence API (JPA)


Bhaskar S 06/03/2012


Introduction

Majority of the enterprise applications build using Java technology, an object-oriented programming platform, involves data persistence to a relational database in some way. The Java Persistence API (JPA) is a specification for persisting (storing and retrieving) application objects to relational store in a standard way. Note that JPA is just a specification and not an implementation. Hibernate, a popular open source persistence framework, implements the JPA specification.

For our quick introduction to JPA, we will need the following software:


Hands-on with Java Persistence API (JPA)

Without much further ado, lets get started. In this example, we will show how to create, read, update, and delete a record in a MySQL database table using a Java POJO.

Create the Contact table in the MySQL database using the following SQL:

CREATE TABLE Contact (
    contactId BIGINT NOT NULL PRIMARY KEY,
    emailId VARCHAR(50) NOT NULL,
    home VARCHAR(15),
    mobile VARCHAR(15)
);

Create the corresponding Contact01 class in Java as follows:

Listing.1
package com.polarsparc.jpa;

public class Contact01 {
private long contactId;

private String emailId;
private String home;
private String mobile;

public long getContactId() {
return contactId;
}

public void setContactId(long contactId) {
this.contactId = contactId;
}

public String getEmailId() {
return emailId;
}

public void setEmailId(String emailId) {
this.emailId = emailId;
}

public String getHome() {
return home;
}

public void setHome(String home) {
this.home = home;
}

public String getMobile() {
return mobile;
}

public void setMobile(String mobile) {
this.mobile = mobile;
}

@Override
public String toString() {
return "[" + emailId + ", " + home + ", " + mobile + "]";
}
}

The Contact01 class listed above is a regular Java POJO.

Next, let us create a Java class called SimpleContactPersist that will persist, query, update, and delete (CRUD) a Contact object using JPA:

Listing.2
package com.polarsparc.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;

public class SimpleContactPersist {
public static void main(String[] args) throws Exception {
Contact01 c1 = new Contact01();
c1.setContactId(1);
c1.setEmailId("foo.bar@email.com");
c1.setMobile("100-200-3000");

EntityManagerFactory emf =
Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Create a contact
em.getTransaction().begin();
em.persist(c1);
em.getTransaction().commit();

System.out.printf("CREATE: Contact %s\n", c1);

// Retrieve the contact
Contact01 c2 = em.find(Contact01.class, c1.getContactId());

System.out.printf("READ: Contact: %s\n", c2);

// Update the contact
em.getTransaction().begin();
c2.setHome("101-201-3001");
em.getTransaction().commit();

System.out.printf("UPDATE: Contact: %s\n", c2);

// Delete the contact
em.getTransaction().begin();
em.remove(c2);
em.getTransaction().commit();

System.out.printf("DELETE: Contact: %s\n", c2);

em.close();
emf.close();
}
}

We will need a JPA interface through which we can interact with the underlying database. This is where the EntityManager comes into play – it manages entity objects by providing methods for performing the CRUD operations.

How does one create an instance of the EntityManager ? This is where the EntityManagerFactory comes into play – it is a factory object for creating the EntityManager.

The next natural question is, how does one create an instance of the EntityManagerFactory ? This is where the bootstrap class called Persistence comes into play.

In the above class called SimpleContactPersist, we have not specified any information regarding the underlying database – where is that coming from ? That information is specified in the configuration file called META-INF/persistence.xml, which has to be in the classpath for the JPA bootstrap class Persistence to load and use during initialization.

NOTE :: The configuration file persistence.xml has to be in the directory META-INF

The following is our META-INF/persistence.xml file:

Listing.3
<?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="PolarSparcJPA" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>

<properties>
<property name="hibernate.show_sql" value="true" />

<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQL5Dialect" />

<property name="hibernate.connection.driver_class"
value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url"
value="jdbc:mysql://localhost:3306/mydb" />
<property name="hibernate.connection.username" value="****" />
<property name="hibernate.connection.password" value="****" />
</properties>
</persistence-unit>
</persistence>

Each instance of EntityManagerFactory is associated with a persistence-unit. A persistence-unit has a name and identifies the following pieces of information:

In our example, we have named our persistence-unit “PolarSparcJPA”. In addition, we have specified the value for transaction-type attribute to be “RESOURCE_LOCAL” which indicates that the EntityManager is responsible for managing the transactions. In other words, we will explicitly manage transactions in the code.

There is one more piece of the JPA puzzle missing - we have not specified any information on how the Contact01 object maps to the underlying database table named Contact. That piece of the information is specified in the object-relational mapping file called META-INF/orm.xml, which has to be in the classpath for JPA to load and use during runtime.

The following is our META-INF/orm.xml file:

Listing.4
<?xml version="1.0" encoding="UTF-8" ?>

<entity-mappings version="2.0"
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
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

<entity class="com.polarsparc.jpa.Contact01" name="Contact01">

<table name="Contact"/>

<attributes>
<id name="contactId">
<column name="contactId" />
</id>

<basic name="emailId">
<column name="emailId" />
</basic>

<basic name="home">
<column name="home" />
</basic>

<basic name="mobile">
<column name="mobile" />
</basic>
</attributes>
</entity>

</entity-mappings>

NOTE :: The configuration file orm.xml has to be in the directory META-INF

The element specifies a Java POJO to be an entity that can be persisted to a database. In our example, the Java POJO Contact01 is defined as a persistence entity.

The <table> element specifies the underlying database table to which an entity is mapped. In our example, the Java POJO Contact01 is mapped to the database table named Contact.

The <id> element specifies the primary key identifier for an entity. In our example, for the Java POJO Contact01, the data member contactId maps to the primary key column contactId of the table Contact.

The <column> element specifies the name of the database column in the underlying database table that the Java POJO data member corresponds to.

The <basic> element maps a non identifier data member of the Java POJO to a non-key database column in the underlying database table.

In order to test our example, let us create a script called bin/contact.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.SimpleContactPersist

If we run the script bin/contact.sh, we see the following output:

Output.1

Jun 03, 2012 2:14:12 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 03, 2012 2:14:12 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 03, 2012 2:14:12 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 03, 2012 2:14:12 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 03, 2012 2:14:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 03, 2012 2:14:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 03, 2012 2:14:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 03, 2012 2:14:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 03, 2012 2:14:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 03, 2012 2:14:13 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 03, 2012 2:14:13 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 03, 2012 2:14:13 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into Contact (emailId, home, mobile, contactId) values (?, ?, ?, ?)
CREATE: Contact [foo.bar@email.com, null, 100-200-3000]
READ: Contact: [foo.bar@email.com, null, 100-200-3000]
Hibernate: update Contact set emailId=?, home=?, mobile=? where contactId=?
UPDATE: Contact: [foo.bar@email.com, 101-201-3001, 100-200-3000]
Hibernate: delete from Contact where contactId=?
DELETE: Contact: [foo.bar@email.com, 101-201-3001, 100-200-3000]
Jun 03, 2012 2:14:13 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/mydb]

We have successfully demonstrated our first example on object persistence using JPA.

In the example above, we used the mapping XML META-INF/orm.xml to map our Java POJO Contact01 to the database table Contact. We can also leverage Java Annotations in the POJO to map the object to an underlying database table and do away with the mapping XML META-INF/orm.xml. Let us explore that option in the following example.

Create the Member table in the MySQL database using the following SQL:

CREATE TABLE Member (
    memberId BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    firstName VARCHAR(30) NOT NULL,
    lastName VARCHAR(30) NOT NULL,
    zip VARCHAR(15)
);

Create the corresponding Member01 class in Java as follows:

Listing.5
package com.polarsparc.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Table;

@Entity
@Table(name="Member")
public class Member01 {
@Id
@GeneratedValue
@Column(name="memberId")
private long memberId;

@Column(name="firstName")
private String firstName;

@Column(name="lastName")
private String lastName;

@Column(name="zip")
private String zip;

public long getMemberId() {
return memberId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

@Override
public String toString() {
return "[" + firstName + ", " + lastName + ", " + zip + "]";
}
}

The Member01 class listed above is a regular Java POJO except for a few annotations.

The @Entity annotation is specified at the class level and indicates that the Member01 class is an entity which can be persisted to the database.

The @Table annotation is specified at the class level and allows us to map the Java entity to a particular database table. In our example, we map the Member01 entity to the Member database table.

The @Id annotation is specified at the field level and identifies the primary key field of an entity. In the Member01 class, the @Id annotation is specified for the memberId field which is the corresponding primary key column in the database table Member.

The @GeneratedValue annotation is specified at the field level and indicates that the value for the field is generated from the database on persistence. In the Member01 class, the @GeneratedValue annotation is specified for the memberId field. Notice that the primary key column memberId is an auto_incremented database column.

The @Column annotation is specified at the field level and allows us to map a Java entity field to a particular database column. In our example, we map the Member01 entity fields memberId, firstName, lastName, and zip to the columns of Member database table memberId, firstName, lastName, and zip respectively.

Next, create a Java class called SimpleMemberPersist that will persist, query, update, and delete (CRUD) a Member object using JPA:

Listing.6
package com.polarsparc.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;

public class SimpleMemberPersist {
public static void main(String[] args) throws Exception {
Member01 m1 = new Member01();
m1.setFirstName("Foo");
m1.setLastName("Bar");
m1.setZip("10001");

EntityManagerFactory emf =
Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Create a member
em.getTransaction().begin();
em.persist(m1);
em.getTransaction().commit();

System.out.printf("CREATE: Member %d, %s\n", m1.getMemberId(), m1);

// Retrieve the member
Member01 m2 = em.find(Member01.class, m1.getMemberId());

System.out.printf("READ: Member: %s\n", m2);

// Update the member
em.getTransaction().begin();
m2.setZip("10005");
em.getTransaction().commit();

System.out.printf("UPDATE: Member: %s\n", m2);

// Delete the member
em.getTransaction().begin();
em.remove(m2);
em.getTransaction().commit();

System.out.printf("DELETE: Member: %s\n", m2);

em.close();
emf.close();
}
}

In order to test our example, let us create a script called bin/member.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.SimpleMemberPersist

If we run the script bin/member.sh, we see the following output:

Output.2

Jun 03, 2012 8:20:53 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 03, 2012 8:20:53 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 03, 2012 8:20:53 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 03, 2012 8:20:53 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 03, 2012 8:20:54 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 03, 2012 8:20:54 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 03, 2012 8:20:54 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 03, 2012 8:20:54 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 03, 2012 8:20:54 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 03, 2012 8:20:54 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 03, 2012 8:20:54 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 03, 2012 8:20:54 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into Member (firstName, lastName, zip) values (?, ?, ?)
CREATE: Member 1, [Foo, Bar, 10001]
READ: Member: [Foo, Bar, 10001]
Hibernate: update Member set firstName=?, lastName=?, zip=? where memberId=?
UPDATE: Member: [Foo, Bar, 10005]
Hibernate: delete from Member where memberId=?
DELETE: Member: [Foo, Bar, 10005]
Jun 03, 2012 8:20:55 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/mydb]

We have successfully demonstrated an example of object persistence using JPA annotations.

Let us move on to explore how to handle entity relationships using JPA.

We will look at one-to-one relationship between a Member entity and a Contact entity. Every member has an associated contact information. In relational database, the one-to-one association between two entities is established with a join column.

The following illustration depicts the one-to-one relationship between a Member entity and a Contact entity:

Member_Contact_01 Image
Figure.1

Drop and create the modified Member table in the MySQL database using the following SQL:

CREATE TABLE Member (
    memberId BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    firstName VARCHAR(30) NOT NULL,
    lastName VARCHAR(30) NOT NULL,
    zip VARCHAR(15)
);

We will demonstrate the one-to-one relationship between the Member entity and the Contact entity using the two approaches: XML mapping file (META-INF/orm.xml) and JPA Annotations.

Let us start with the XML mapping file (META-INF/orm.xml) approach.

Create the corresponding Member02 class in Java as follows:

Listing.7
package com.polarsparc.jpa;

public class Member02 {
private long memberId;

private String firstName;
private String lastName;
private String zip;

private Contact01 contact;

public long getMemberId() {
return memberId;
}

public void setMemberId(long memberId) {
this.memberId = memberId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

public Contact01 getContact() {
return contact;
}

public void setContact(Contact01 contact) {
this.contact = contact;
}

@Override
public String toString() {
return "[" + firstName + ", " + lastName + ", " + zip + ", <" + contact + ">]";
}
}

The Member02 class listed above is a regular Java POJO.

Next, we need to modify the object-relational mapping file META-INF/orm.xml to specify how the Member02 object maps to the underlying database table named Member as well as to specify the one-to-one entity relationship between the Member entity and the Contact entity.

The following is our META-INF/orm.xml file:

Listing.8
<?xml version="1.0" encoding="UTF-8" ?>

<entity-mappings version="2.0"
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
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

<entity class="com.polarsparc.jpa.Contact01" name="Contact01">
<table name="Contact"/>

<attributes>
<id name="contactId">
<column name="contactId" />
</id>

<basic name="emailId">
<column name="emailId" />
</basic>

<basic name="home">
<column name="home" />
</basic>

<basic name="mobile">
<column name="mobile" />
</basic>
</attributes>
</entity>

<entity class="com.polarsparc.jpa.Member02" name="Member02">
<table name="Member"/>

<attributes>
<id name="memberId">
<column name="memberId" />
<generated-value strategy="AUTO" />
</id>

<basic name="firstName">
<column name="firstName" />
</basic>

<basic name="lastName">
<column name="lastName" />
</basic>

<basic name="zip">
<column name="zip" />
</basic>

<one-to-one name="contact">
<join-column name="memberContactId" />
<cascade>
<cascade-all />
</cascade>
</one-to-one>
</attributes>
</entity>

</entity-mappings>

The <generated-value> element indicates that the value for the field is generated from the database on persistence.

The <one-to-one> element specifies the one-to-one association between the Member entity and the Contact entity.

The <join-column> element indicates the foreign key column.

The <cascade> element with <cascade-all> indicates that operations should automatically be cascaded across relationships, which in our case is between the Member and Contact entities.

Next, create a Java class called MemberContactPersist01 that will persist, query, update, and delete (CRUD) both the Member and Contact objects using JPA:

Listing.9
package com.polarsparc.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;

public class MemberContactPersist01 {
public static void main(String[] args) throws Exception {
Contact01 c1 = new Contact01();
c1.setContactId(1);
c1.setEmailId("foo.bar@email.com");
c1.setMobile("100-200-3000");

Member02 m1 = new Member02();
m1.setFirstName("Foo");
m1.setLastName("Bar");
m1.setZip("10001");
m1.setContact(c1);

EntityManagerFactory emf =
Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Create a member and contact
em.getTransaction().begin();
em.persist(m1);
em.getTransaction().commit();

System.out.printf("CREATE: Member+Contact %d, %s\n", m1.getMemberId(), m1);

// Retrieve the member and contact
Member02 m2 = em.find(Member02.class, m1.getMemberId());

System.out.printf("READ: Member+Contact: %s\n", m2);

// Update the member and contact
em.getTransaction().begin();
m2.setZip("10005");
m2.getContact().setHome("101-201-3001");
em.getTransaction().commit();

System.out.printf("UPDATE: Member+Contact: %s\n", m2);

// Delete the member and contact
em.getTransaction().begin();
em.remove(m2);
em.getTransaction().commit();

System.out.printf("DELETE: Member+Contact: %s\n", m2);

em.close();
emf.close();
}
}

In order to test our example, let us create a script called bin/memcon01.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.MemberContactPersist01

If we run the script bin/memcon01.sh, we see the following output:

Output.3

Jun 10, 2012 1:07:41 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 10, 2012 1:07:41 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 10, 2012 1:07:41 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 10, 2012 1:07:41 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 10, 2012 1:07:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 10, 2012 1:07:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 10, 2012 1:07:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 10, 2012 1:07:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 10, 2012 1:07:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 10, 2012 1:07:42 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 10, 2012 1:07:42 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 10, 2012 1:07:42 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into Contact (emailId, home, mobile, contactId) values (?, ?, ?, ?)
Hibernate: insert into Member (memberContactId, firstName, lastName, zip) values (?, ?, ?, ?)
CREATE: Member+Contact 6, [Foo, Bar, 10001, <[foo.bar@email.com, null, 100-200-3000]>]
READ: Member+Contact: [Foo, Bar, 10001, <[foo.bar@email.com, null, 100-200-3000]>]
Hibernate: update Member set memberContactId=?, firstName=?, lastName=?, zip=? where memberId=?
Hibernate: update Contact set emailId=?, home=?, mobile=? where contactId=?
UPDATE: Member+Contact: [Foo, Bar, 10005, <[foo.bar@email.com, 101-201-3001, 100-200-3000]>]
Hibernate: delete from Member where memberId=?
Hibernate: delete from Contact where contactId=?
DELETE: Member+Contact: [Foo, Bar, 10005, <[foo.bar@email.com, 101-201-3001, 100-200-3000]>]
Jun 10, 2012 1:07:43 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/mydb]

CAUTION :: If we omit the <cascade-all> element, we will encounter the following exception: Caused by: java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing: com.polarsparc.jpa.Member.contact -> com.polarsparc.jpa.Contact

We have successfully demonstrated an example for one-to-one relationship using the XML mapping file approach in JPA.

Now let us demonstrate the same example using the JPA Annotations approach.

Create the corresponding Member03 class in Java as follows:

Listing.10
package com.polarsparc.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Table;
import javax.persistence.OneToOne;
import javax.persistence.CascadeType;
import javax.persistence.JoinColumn;

@Entity
@Table(name="Member")
public class Member03 {
@Id
@GeneratedValue
@Column(name="memberId")
private long memberId;

@Column(name="firstName")
private String firstName;

@Column(name="lastName")
private String lastName;

@Column(name="zip")
private String zip;

@OneToOne(cascade={CascadeType.ALL})
@JoinColumn(name="memberContactId")
private Contact01 contact;

public long getMemberId() {
return memberId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

public Contact01 getContact() {
return contact;
}

public void setContact(Contact01 contact) {
this.contact = contact;
}

@Override
public String toString() {
return "[" + firstName + ", " + lastName + ", " + zip + ", <" + contact + ">]";
}
}

The new Member03 class listed above introduces two new annotations.

The @OneToOne annotation specifies the one-to-one association between the Member entity and the Contact entity. The cascade={CascadeType.ALL} attribute indicates that operations should automatically be cascaded across associations.

The @JoinColumn annotation indicates the foreign key column. The name=”memberContactId” attribute specifies the join column.

Next, create a Java class called MemberContactPersist02 that will persist, query, update, and delete (CRUD) both the Member and Contact objects using JPA:

Listing.11
package com.polarsparc.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;

public class MemberContactPersist02 {
public static void main(String[] args) throws Exception {
Contact01 c1 = new Contact01();
c1.setContactId(1);
c1.setEmailId("foo.bar@email.com");
c1.setMobile("100-200-3000");

Member03 m1 = new Member03();
m1.setFirstName("Foo");
m1.setLastName("Bar");
m1.setZip("10001");
m1.setContact(c1);

EntityManagerFactory emf =
Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Create a member and contact
em.getTransaction().begin();
em.persist(m1);
em.getTransaction().commit();

System.out.printf("CREATE: Member+Contact %d, %s\n", m1.getMemberId(), m1);

// Retrieve the member and contact
Member03 m2 = em.find(Member03.class, m1.getMemberId());

System.out.printf("READ: Member+Contact: %s\n", m2);

// Update the member and contact
em.getTransaction().begin();
m2.setZip("10005");
m2.getContact().setHome("101-201-3001");
em.getTransaction().commit();

System.out.printf("UPDATE: Member+Contact: %s\n", m2);

// Delete the member and contact
em.getTransaction().begin();
em.remove(m2);
em.getTransaction().commit();

System.out.printf("DELETE: Member+Contact: %s\n", m2);

em.close();
emf.close();
}
}

In order to test our example, let us create a script called bin/memcon02.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.MemberContactPersist02

If we run the script bin/memcon02.sh, we see the following output:

Output.4

Jun 10, 2012 1:21:08 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 10, 2012 1:21:08 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 10, 2012 1:21:08 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 10, 2012 1:21:08 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 10, 2012 1:21:09 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 10, 2012 1:21:09 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 10, 2012 1:21:09 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 10, 2012 1:21:09 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 10, 2012 1:21:09 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 10, 2012 1:21:09 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 10, 2012 1:21:09 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 10, 2012 1:21:09 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into Contact (emailId, home, mobile, contactId) values (?, ?, ?, ?)
Hibernate: insert into Member (memberContactId, firstName, lastName, zip) values (?, ?, ?, ?)
CREATE: Member+Contact 8, [Foo, Bar, 10001, <[foo.bar@email.com, null, 100-200-3000]>]
READ: Member+Contact: [Foo, Bar, 10001, <[foo.bar@email.com, null, 100-200-3000]>]
Hibernate: update Member set memberContactId=?, firstName=?, lastName=?, zip=? where memberId=?
Hibernate: update Contact set emailId=?, home=?, mobile=? where contactId=?
UPDATE: Member+Contact: [Foo, Bar, 10005, <[foo.bar@email.com, 101-201-3001, 100-200-3000]>]
Hibernate: delete from Member where memberId=?
Hibernate: delete from Contact where contactId=?
DELETE: Member+Contact: [Foo, Bar, 10005, <[foo.bar@email.com, 101-201-3001, 100-200-3000]>]
Jun 10, 2012 1:21:09 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/mydb]

CAUTION :: If we omit the cascade={CascadeType.ALL} attribute, we will encounter the following exception: Caused by: java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing: com.polarsparc.jpa.Member.contact -> com.polarsparc.jpa.Contact

We have successfully demonstrated an example for one-to-one relationship using JPA Annotations approach.

The one-to-one association we have seen in the above example is unidirectional, that is, we can access the Contact entity only via the Member entity.

We will look at the bidirectional one-to-one relation between the Member and Contact entities using the XML mapping file (META-INF/orm.xml) approach.

Create the corresponding Contact02 class in Java as follows:

Listing.12
package com.polarsparc.jpa;

public class Contact02 {
private long contactId;

private String emailId;
private String home;
private String mobile;

private Member04 member4;

public long getContactId() {
return contactId;
}

public void setContactId(long contactId) {
this.contactId = contactId;
}

public String getEmailId() {
return emailId;
}

public void setEmailId(String emailId) {
this.emailId = emailId;
}

public String getHome() {
return home;
}

public void setHome(String home) {
this.home = home;
}

public String getMobile() {
return mobile;
}

public void setMobile(String mobile) {
this.mobile = mobile;
}

public Member04 getMember4() {
return member4;
}

public void setMember4(Member04 member) {
this.member4 = member;
}

@Override
public String toString() {
return "[" + emailId + ", " + home + ", " + mobile + "]";
}
}

The Contact02 class listed above is a regular Java POJO.

Also, create the corresponding Member04 class in Java as follows:

Listing.13
package com.polarsparc.jpa;

public class Member04 {
private long memberId;

private String firstName;
private String lastName;
private String zip;

private Contact02 contact2;

public long getMemberId() {
return memberId;
}

public void setMemberId(long memberId) {
this.memberId = memberId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

public Contact02 getContact2() {
return contact2;
}

public void setContact2(Contact02 contact) {
this.contact2 = contact;
}

@Override
public String toString() {
return "[" + firstName + ", " + lastName + ", " + zip + "]";
}
}

The Member04 class listed above is a regular Java POJO.

Next, we need to modify the object-relational mapping file META-INF/orm.xml to specify the bidirectional one-to-one entity relationship between the Member entity and the Contact entity.

The following is our META-INF/orm.xml file:

Listing.14
<?xml version="1.0" encoding="UTF-8" ?>

<entity-mappings version="2.0"
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
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

<entity class="com.polarsparc.jpa.Contact01" name="Contact01">
<table name="Contact"/>

<attributes>
<id name="contactId">
<column name="contactId" />
</id>

<basic name="emailId">
<column name="emailId" />
</basic>

<basic name="home">
<column name="home" />
</basic>

<basic name="mobile">
<column name="mobile" />
</basic>
</attributes>
</entity>

<entity class="com.polarsparc.jpa.Member02" name="Member02">
<table name="Member"/>

<attributes>
<id name="memberId">
<column name="memberId" />
<generated-value strategy="AUTO" />
</id>

<basic name="firstName">
<column name="firstName" />
</basic>

<basic name="lastName">
<column name="lastName" />
</basic>

<basic name="zip">
<column name="zip" />
</basic>

<one-to-one name="contact">
<join-column name="memberContactId" />
<cascade>
<cascade-all />
</cascade>
</one-to-one>
</attributes>
</entity>

<entity class="com.polarsparc.jpa.Contact02" name="Contact02">
<table name="Contact"/>

<attributes>
<id name="contactId">
<column name="contactId" />
</id>

<basic name="emailId">
<column name="emailId" />
</basic>

<basic name="home">
<column name="home" />
</basic>

<basic name="mobile">
<column name="mobile" />
</basic>

<one-to-one name="member4" mapped-by="contact2">
<cascade>
<cascade-all />
</cascade>
</one-to-one>
</attributes>
</entity>

<entity class="com.polarsparc.jpa.Member04" name="Member04">
<table name="Member"/>

<attributes>
<id name="memberId">
<column name="memberId" />
<generated-value strategy="AUTO" />
</id>

<basic name="firstName">
<column name="firstName" />
</basic>

<basic name="lastName">
<column name="lastName" />
</basic>

<basic name="zip">
<column name="zip" />
</basic>

<one-to-one name="contact2">
<join-column name="memberContactId" />
<cascade>
<cascade-all />
</cascade>
</one-to-one>
</attributes>
</entity>

</entity-mappings>

The mapped-by=”contact2” attribute indicates that the one-to-one association between the Member entity and the Contact entity is specified in the Member entity.

Now, create a Java class called MemberContactPersist03 that will persist, query, update, and delete (CRUD) both the Member and Contact objects using JPA:

Listing.15
package com.polarsparc.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;

public class MemberContactPersist03 {
public static void main(String[] args) throws Exception {
Contact02 c1 = new Contact02();
c1.setContactId(1);
c1.setEmailId("foo.bar@email.com");
c1.setMobile("100-200-3000");

Member04 m1 = new Member04();
m1.setFirstName("Foo");
m1.setLastName("Bar");
m1.setZip("10001");

m1.setContact2(c1);

c1.setMember4(m1);

EntityManagerFactory emf =
Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Create a member and contact
em.getTransaction().begin();
em.persist(m1);
em.getTransaction().commit();

System.out.printf("CREATE (Member): Member+Contact %d, %s\n", m1.getMemberId(), m1);

// Retrieve the member and contact through member
Member04 m2 = em.find(Member04.class, m1.getMemberId());

System.out.printf("READ (Member): Member+Contact: %s\n", m2);

// Retrieve the member and contact through contact
Contact02 c2 = em.find(Contact02.class, c1.getContactId());

System.out.printf("READ (Contact): Member+Contact: %s\n", c2);

// Update the member and contact
em.getTransaction().begin();
m2.setZip("10005");
c2.setHome("101-201-3001");
em.getTransaction().commit();

System.out.printf("UPDATE (Member, Contact): Member+Contact: %s\n", m2);

// Delete the member and contact through contact
em.getTransaction().begin();
em.remove(c2);
em.getTransaction().commit();

System.out.printf("DELETE (Contact): Member+Contact: %s\n", m2);

em.close();
emf.close();
}
}

In order to test our example, let us create a script called bin/memcon03.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.MemberContactPersist03

If we run the script bin/memcon03.sh, we see the following output:

Output.5

Jun 10, 2012 5:54:00 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 10, 2012 5:54:00 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 10, 2012 5:54:00 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 10, 2012 5:54:00 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 10, 2012 5:54:00 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 10, 2012 5:54:00 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 10, 2012 5:54:00 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 10, 2012 5:54:00 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 10, 2012 5:54:00 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 10, 2012 5:54:00 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 10, 2012 5:54:00 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 10, 2012 5:54:00 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into Contact (emailId, home, mobile, contactId) values (?, ?, ?, ?)
Hibernate: insert into Member (memberContactId, firstName, lastName, zip) values (?, ?, ?, ?)
CREATE (Member): Member+Contact 21, [Foo, Bar, 10001]
READ (Member): Member+Contact: [Foo, Bar, 10001]
READ (Contact): Member+Contact: [foo.bar@email.com, null, 100-200-3000]
Hibernate: update Member set memberContactId=?, firstName=?, lastName=?, zip=? where memberId=?
Hibernate: update Contact set emailId=?, home=?, mobile=? where contactId=?
UPDATE (Member, Contact): Member+Contact: [Foo, Bar, 10005]
Hibernate: delete from Member where memberId=?
Hibernate: delete from Contact where contactId=?
DELETE (Contact): Member+Contact: [Foo, Bar, 10005]
Jun 10, 2012 5:54:01 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/mydb]

We have successfully demonstrated an example for bidirectional one-to-one relationship using the XML mapping file approach in JPA.

Now, we will look at the bidirectional one-to-one relation between the Member and Contact entities using the JPA Annotations approach.

Create the corresponding Contact03 class in Java as follows:

Listing.16
package com.polarsparc.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.OneToOne;

@Entity
@Table(name="Contact")
public class Contact03 {
@Id
@Column(name="contactId")
private long contactId;

@Column(name="emailId")
private String emailId;

@Column(name="home")
private String home;

@Column(name="mobile")
private String mobile;

@OneToOne(cascade={CascadeType.ALL}, mappedBy="contact3")
private Member05 member5;

public long getContactId() {
return contactId;
}

public void setContactId(long contactId) {
this.contactId = contactId;
}

public String getEmailId() {
return emailId;
}

public void setEmailId(String emailId) {
this.emailId = emailId;
}

public String getHome() {
return home;
}

public void setHome(String home) {
this.home = home;
}

public String getMobile() {
return mobile;
}

public void setMobile(String mobile) {
this.mobile = mobile;
}

public Member05 getMember5() {
return member5;
}

public void setMember5(Member05 member) {
this.member5 = member;
}

@Override
public String toString() {
return "[" + emailId + ", " + home + ", " + mobile + "]";
}
}

The mappedBy=”contact3” attribute indicates that the one-to-one association between the Member entity and the Contact entity is specified in the Member entity.

Also, create the corresponding Member05 class in Java as follows:

Listing.17
package com.polarsparc.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Table;
import javax.persistence.OneToOne;
import javax.persistence.CascadeType;
import javax.persistence.JoinColumn;

@Entity
@Table(name="Member")
public class Member05 {
@Id
@GeneratedValue
@Column(name="memberId")
private long memberId;

@Column(name="firstName")
private String firstName;

@Column(name="lastName")
private String lastName;

@Column(name="zip")
private String zip;

@OneToOne(cascade={CascadeType.ALL})
@JoinColumn(name="memberContactId")
private Contact03 contact3;

public long getMemberId() {
return memberId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

public Contact03 getContact() {
return contact3;
}

public void setContact(Contact03 contact) {
this.contact3 = contact;
}

@Override
public String toString() {
return "[" + firstName + ", " + lastName + ", " + zip + "]";
}
}

Finally, create a Java class called MemberContactPersist04 that will persist, query, update, and delete (CRUD) both the Member and Contact objects using JPA:

Listing.18
package com.polarsparc.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;

public class MemberContactPersist04 {
public static void main(String[] args) throws Exception {
Contact03 c1 = new Contact03();
c1.setContactId(1);
c1.setEmailId("foo.bar@email.com");
c1.setMobile("100-200-3000");

Member05 m1 = new Member05();
m1.setFirstName("Foo");
m1.setLastName("Bar");
m1.setZip("10001");

m1.setContact(c1);

c1.setMember5(m1);

EntityManagerFactory emf =
Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Create a member and contact
em.getTransaction().begin();
em.persist(m1);
em.getTransaction().commit();

System.out.printf("CREATE (Member): Member+Contact %d, %s\n", m1.getMemberId(), m1);

// Retrieve the member and contact through member
Member05 m2 = em.find(Member05.class, m1.getMemberId());

System.out.printf("READ (Member): Member+Contact: %s\n", m2);

// Retrieve the member and contact through contact
Contact03 c2 = em.find(Contact03.class, c1.getContactId());

System.out.printf("READ (Contact): Member+Contact: %s\n", c2);

// Update the member and contact
em.getTransaction().begin();
m2.setZip("10005");
c2.setHome("101-201-3001");
em.getTransaction().commit();

System.out.printf("UPDATE (Member, Contact): Member+Contact: %s\n", m2);

// Delete the member and contact through contact
em.getTransaction().begin();
em.remove(c2);
em.getTransaction().commit();

System.out.printf("DELETE (Contact): Member+Contact: %s\n", m2);

em.close();
emf.close();
}
}

In order to test our example, let us create a script called bin/memcon04.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.MemberContactPersist04

If we run the script bin/memcon04.sh, we see the following output:

Output.6

Jun 10, 2012 9:01:37 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 10, 2012 9:01:37 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 10, 2012 9:01:37 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 10, 2012 9:01:37 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 10, 2012 9:01:37 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 10, 2012 9:01:37 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 10, 2012 9:01:37 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 10, 2012 9:01:37 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 10, 2012 9:01:37 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 10, 2012 9:01:38 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 10, 2012 9:01:38 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 10, 2012 9:01:38 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into Contact (emailId, home, mobile, contactId) values (?, ?, ?, ?)
Hibernate: insert into Member (memberContactId, firstName, lastName, zip) values (?, ?, ?, ?)
CREATE (Member): Member+Contact 25, [Foo, Bar, 10001]
READ (Member): Member+Contact: [Foo, Bar, 10001]
READ (Contact): Member+Contact: [foo.bar@email.com, null, 100-200-3000]
Hibernate: update Member set memberContactId=?, firstName=?, lastName=?, zip=? where memberId=?
Hibernate: update Contact set emailId=?, home=?, mobile=? where contactId=?
UPDATE (Member, Contact): Member+Contact: [Foo, Bar, 10005]
Hibernate: delete from Member where memberId=?
Hibernate: delete from Contact where contactId=?
DELETE (Contact): Member+Contact: [Foo, Bar, 10005]
Jun 10, 2012 9:01:38 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/mydb]

We have successfully demonstrated an example for bidirectional one-to-one relationship using JPA Annotations approach.

Now let us look at one-to-many bidirectional relationship between an Account entity and the Member entity. Every account has associated with it one or more members. In relational database, the one-to-many bidirectional association between two entities is established with a join table.

The following illustration depicts the one-to-many bidirectional relationship between an Account entity and the Member entity:

Account_Member_01 Image
Figure.2

Drop and create the modified Member as well as the ACCOUNT_TBL and ACCT_MEMBER_TBL tables in the MySQL database using the following SQL:

DROP TABLE Member;

CREATE TABLE Member (
    memberId BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    firstName VARCHAR(30) NOT NULL,
    lastName VARCHAR(30) NOT NULL,
    zip VARCHAR(15)
);

CREATE TABLE ACCOUNT_TBL (
    ACCT_ID VARCHAR(10) NOT NULL PRIMARY KEY,
    ACCT_TYPE VARCHAR(30) NOT NULL,
    BALANCE DOUBLE
);

CREATE TABLE ACCT_MEMBER_TBL (
    ACCT_ID VARCHAR(10),
    MEMBER_ID BIGINT,
    CONSTRAINT Account_FK FOREIGN KEY (ACCT_ID) REFERENCES ACCOUNT_TBL(ACCT_ID),
    CONSTRAINT Member_FK FOREIGN KEY (MEMBER_ID) REFERENCES Member(memberId)
);

We will demonstrate the one-to-many bidirectional relationship between the Account entity and the Member entity using the two approaches: XML mapping file (META-INF/orm.xml) and JPA Annotations.

Let us start with the XML mapping file (META-INF/orm.xml) approach.

Create the corresponding Member06 class in Java as follows:

Listing.19
package com.polarsparc.jpa;

public class Member06 {
private long memberId;

private String firstName;
private String lastName;
private String zip;

private Account01 account1;

public long getMemberId() {
return memberId;
}

public void setMemberId(long memberId) {
this.memberId = memberId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

public Account01 getAccount1() {
return account1;
}

public void setAccount1(Account01 account) {
this.account1 = account;
}

@Override
public String toString() {
return "[" + firstName + ", " + lastName + ", " + zip + ", <" + account1.getAccountId() + ">]";
}
}

The Member06 class listed above is a regular Java POJO.

Next, create the Account01 class in Java as follows:

Listing.20
package com.polarsparc.jpa;

import java.util.*;

public class Account01 {
private String accountId;

private String accountType;

private double balance;

private Collection<Member06> members6 = new ArrayList<Member06>();

public String getAccountId() {
return accountId;
}

public void setAccountId(String accountId) {
this.accountId = accountId;
}

public String getAccountType() {
return accountType;
}

public void setAccountType(String accountType) {
this.accountType = accountType;
}

public double getBalance() {
return balance;
}

public void setBalance(double balance) {
this.balance = balance;
}

public Collection<Member06> getMembers6() {
return members6;
}

public void setMembers6(List<Member06> members) {
this.members6 = members;
}

@Override
public String toString() {
return "[" + accountId + ", " + accountType + ", " + balance + ", <" + members6 + ">]";
}
}

The Account01 class listed above is a regular Java POJO.

Next, we need to modify the object-relational mapping file META-INF/orm.xml to specify the bidirectional one-to-many entity relationship between the Account entity and the Member entity.

The following is our META-INF/orm.xml file:

Listing.21
<?xml version="1.0" encoding="UTF-8" ?>

<entity-mappings version="2.0"
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
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

<entity class="com.polarsparc.jpa.Account01" name="Account01">
<table name="ACCOUNT_TBL"/>

<attributes>
<id name="accountId">
<column name="ACCT_ID" />
</id>

<basic name="accountType">
<column name="ACCT_TYPE" />
</basic>

<basic name="balance">
<column name="BALANCE" />
</basic>

<one-to-many name="members6" mapped-by="account1">
<cascade>
<cascade-all />
</cascade>
</one-to-many>
</attributes>
</entity>

<entity class="com.polarsparc.jpa.Member06" name="Member06">
<table name="Member"/>

<attributes>
<id name="memberId">
<column name="memberId" />
<generated-value strategy="AUTO" />
</id>

<basic name="firstName">
<column name="firstName" />
</basic>

<basic name="lastName">
<column name="lastName" />
</basic>

<basic name="zip">
<column name="zip" />
</basic>

<many-to-one name="account1">
<join-table name="ACCT_MEMBER_TBL">
<join-column name="MEMBER_ID" />
<inverse-join-column name="ACCT_ID" />
</join-table>
<cascade>
<cascade-all />
</cascade>
</many-to-one>
</attributes>
</entity>

</entity-mappings>

The <one-to-many> element specifies the one-to-many association between the Account entity and the Member entity.

The mapped-by=”account1” attribute indicates that the one-to-many association between the Account entity and the Member entity is specified in the Member entity.

The <join-table> element indicates the use of a join table called ACCT_MEMBER_TBL to associate an Account with its Members.

The <join-column> element indicates the foreign key column of the Member entity.

The <inverse-join-column> element indicates the foreign key column of the other (inverse side) Account entity.

The <many-to-one> element specifies the mant-to-one association between the Member entity and the Account entity.

Next, create a Java class called AccountMemberPersist01 that will persist, query, update, and delete (CRUD) both the Account and Member objects using JPA:

Listing.22
package com.polarsparc.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;

public class AccountMemberPersist01 {
public static void main(String[] args) throws Exception {
Account01 a1 = new Account01();
a1.setAccountId("A001");
a1.setAccountType("Savings");
a1.setBalance(250.00);

Member06 m1 = new Member06();
m1.setFirstName("Foo");
m1.setLastName("Bar");
m1.setZip("10001");

Member06 m2 = new Member06();
m2.setFirstName("Bar");
m2.setLastName("Foo");
m2.setZip("10001");

a1.getMembers6().add(m1);

m1.setAccount1(a1);

EntityManagerFactory emf = Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Create an account with a member
em.getTransaction().begin();
em.persist(a1);
em.getTransaction().commit();

System.out.printf("CREATE <Account>: Account+Member %s, %s\n", a1.getAccountId(), a1);

// Retrieve the account and member(s) through account
Account01 a2 = em.find(Account01.class, a1.getAccountId());

System.out.printf("READ <Account>: Account+Member: %s\n", a2);

// Update the account and member(s) through account
em.getTransaction().begin();
m2.setAccount1(a2);
a2.getMembers6().add(m2);
em.getTransaction().commit();

System.out.printf("UPDATE <Account>: Account+Member: %s\n", a2);

// Retrieve the member and the account through member
Member06 m3 = em.find(Member06.class, m1.getMemberId());

System.out.printf("READ <Member>: Member+Account: %s, %s\n", m3, m3.getAccount1());

// Delete the account and member(s) through member
em.getTransaction().begin();
em.remove(m3);
em.getTransaction().commit();

System.out.printf("DELETE <Member>: Member+Account: %s\n", a2);

em.close();
emf.close();

}
}

In order to test our example, let us create a script called bin/acctmem01.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.AccountMemberPersist01

If we run the script bin/acctmem01.sh, we see the following output:

Output.7

Jun 16, 2012 8:54:18 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 16, 2012 8:54:18 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 16, 2012 8:54:18 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 16, 2012 8:54:18 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 16, 2012 8:54:19 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 16, 2012 8:54:19 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 16, 2012 8:54:19 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 16, 2012 8:54:19 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 16, 2012 8:54:19 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 16, 2012 8:54:19 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 16, 2012 8:54:19 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 16, 2012 8:54:19 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into ACCOUNT_TBL (ACCT_TYPE, BALANCE, ACCT_ID) values (?, ?, ?)
Hibernate: insert into Member (firstName, lastName, zip) values (?, ?, ?)
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
CREATE <Account>: Account+Member A001, [A001, Savings, 250.0, <[[Foo, Bar, 10001, <A001>]]>]
READ <Account>: Account+Member: [A001, Savings, 250.0, <[[Foo, Bar, 10001, <A001>]]>]
Hibernate: insert into Member (firstName, lastName, zip) values (?, ?, ?)
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
Hibernate: delete from ACCT_MEMBER_TBL where ACCT_ID=?
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
UPDATE <Account>: Account+Member: [A001, Savings, 250.0, <[[Foo, Bar, 10001, <A001>], [Bar, Foo, 10001, <A001>]]>]
READ <Member>: Member+Account: [Foo, Bar, 10001, <A001>], [A001, Savings, 250.0, <[[Foo, Bar, 10001, <A001>], [Bar, Foo, 10001, <A001>]]>]
Hibernate: delete from ACCT_MEMBER_TBL where ACCT_ID=?
Hibernate: delete from ACCT_MEMBER_TBL where MEMBER_ID=?
Hibernate: delete from Member where memberId=?
Hibernate: delete from ACCT_MEMBER_TBL where MEMBER_ID=?
Hibernate: delete from Member where memberId=?
Hibernate: delete from ACCOUNT_TBL where ACCT_ID=?
DELETE <Member>: Member+Account: [A001, Savings, 250.0, <[[Foo, Bar, 10001, <A001>], [Bar, Foo, 10001, <A001>]]>]
Jun 16, 2012 8:54:20 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/mydb]

We have successfully demonstrated an example for bidirectional one-to-many bidirectional relationship using the XML mapping file approach in JPA.

Now, we will look at the bidirectional one-to-many relation between the Account and Member entities using the JPA Annotations approach.

Create the corresponding Account02 class in Java as follows:

Listing.23
package com.polarsparc.jpa;

import java.util.*;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.OneToMany;
import javax.persistence.CascadeType;

@Entity
@Table(name="ACCOUNT_TBL")
public class Account02 {
@Id
@Column(name="ACCT_ID")
private String accountId;

@Column(name="ACCT_TYPE")
private String accountType;

@Column(name="BALANCE")
private double balance;

@OneToMany(cascade={CascadeType.ALL}, mappedBy="account2")
private Collection<Member07> members7 = new ArrayList<Member07>();

public String getAccountId() {
return accountId;
}

public void setAccountId(String accountId) {
this.accountId = accountId;
}

public String getAccountType() {
return accountType;
}

public void setAccountType(String accountType) {
this.accountType = accountType;
}

public double getBalance() {
return balance;
}

public void setBalance(double balance) {
this.balance = balance;
}

public Collection<Member07> getMembers7() {
return members7;
}

public void setMembers6(Collection<Member07> members) {
this.members7 = members;
}

@Override
public String toString() {
return "[" + accountId + ", " + accountType + ", " + balance + ", <" + members7 + ">]";
}
}

The @OneToMany annotation specifies the one-to-many association between the Account entity and the Member entity.

The mappedBy=”account2” attribute indicates that the one-to-many association between the Account entity and the Member entity is specified in the Member entity.

Also, create the corresponding Member07 class in Java as follows:

Listing.24
package com.polarsparc.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Table;
import javax.persistence.ManyToOne;
import javax.persistence.CascadeType;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;

@Entity
@Table(name="Member")
public class Member07 {
@Id
@GeneratedValue
@Column(name="memberId")
private long memberId;

@Column(name="firstName")
private String firstName;

@Column(name="lastName")
private String lastName;

@Column(name="zip")
private String zip;

@ManyToOne(cascade={CascadeType.ALL})
@JoinTable(name="ACCT_MEMBER_TBL",
joinColumns={@JoinColumn(name="MEMBER_ID")},
inverseJoinColumns={@JoinColumn(name="ACCT_ID")})
private Account02 account2;

public long getMemberId() {
return memberId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

public Account02 getAccount2() {
return account2;
}

public void setAccount2(Account02 account) {
this.account2 = account;
}

@Override
public String toString() {
return "[" + firstName + ", " + lastName + ", " + zip + "<" +
account2.getAccountId() + ">]";
}
}

The @ManyToOne annotation specifies the many-to-one association between the Member entity and the Account entity.

The joinColumns={@JoinColumn(name="MEMBER_ID")} attribute element indicates the foreign key column (MEMBER_ID) of the Member entity.

The inverseJoinColumns={@JoinColumn(name="ACCT_ID")} attribute indicates the foreign key column (ACCT_ID) of the other (inverse side) Account entity.

Finally, create a Java class called AccountMemberPersist02 that will persist, query, update, and delete (CRUD) both the Member and Contact objects using JPA:

Listing.25
package com.polarsparc.jpa;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;

public class AccountMemberPersist02 {
public static void main(String[] args) throws Exception {
Account02 a1 = new Account02();
a1.setAccountId("A001");
a1.setAccountType("Savings");
a1.setBalance(250.00);

Member07 m1 = new Member07();
m1.setFirstName("Foo");
m1.setLastName("Bar");
m1.setZip("10001");

Member07 m2 = new Member07();
m2.setFirstName("Bar");
m2.setLastName("Foo");
m2.setZip("10001");

a1.getMembers7().add(m1);

m1.setAccount2(a1);

EntityManagerFactory emf = Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Create an account with a member
em.getTransaction().begin();
em.persist(a1);
em.getTransaction().commit();

System.out.printf("CREATE <Account>: Account+Member %s, %s\n", a1.getAccountId(), a1);

// Retrieve the account and member(s) through account
Account02 a2 = em.find(Account02.class, a1.getAccountId());

System.out.printf("READ <Account>: Account+Member: %s\n", a2);

// Update the account and member(s) through account
em.getTransaction().begin();
m2.setAccount2(a2);
a2.getMembers7().add(m2);
em.getTransaction().commit();

System.out.printf("UPDATE <Account>: Account+Member: %s\n", a2);

// Retrieve the member and the account through member
Member07 m3 = em.find(Member07.class, m1.getMemberId());

System.out.printf("READ <Member>: Member+Account: %s, %s\n", m3, m3.getAccount2());

// Delete the account and member(s) through member
em.getTransaction().begin();
em.remove(m3);
em.getTransaction().commit();

System.out.printf("DELETE <Member>: Member+Account: %s\n", a2);

em.close();
emf.close();

}
}

In order to test our example, let us create a script called bin/acctmem02.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.AccountMemberPersist02

If we run the script bin/acctmem02.sh, we see the following output:

Output.8

Jun 16, 2012 10:49:11 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 16, 2012 10:49:11 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 16, 2012 10:49:11 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 16, 2012 10:49:11 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 16, 2012 10:49:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 16, 2012 10:49:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 16, 2012 10:49:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 16, 2012 10:49:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 16, 2012 10:49:12 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 16, 2012 10:49:12 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 16, 2012 10:49:12 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 16, 2012 10:49:12 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: insert into ACCOUNT_TBL (ACCT_TYPE, BALANCE, ACCT_ID) values (?, ?, ?)
Hibernate: insert into Member (firstName, lastName, zip) values (?, ?, ?)
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
CREATE <Account>: Account+Member A001, [A001, Savings, 250.0, <[[Foo, Bar, 10001<A001>]]>]
READ <Account>: Account+Member: [A001, Savings, 250.0, <[[Foo, Bar, 10001<A001>]]>]
Hibernate: insert into Member (firstName, lastName, zip) values (?, ?, ?)
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
Hibernate: delete from ACCT_MEMBER_TBL where ACCT_ID=?
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
Hibernate: insert into ACCT_MEMBER_TBL (ACCT_ID, MEMBER_ID) values (?, ?)
UPDATE <Account>: Account+Member: [A001, Savings, 250.0, <[[Foo, Bar, 10001<A001>], [Bar, Foo, 10001<A001>]]>]
READ <Member>: Member+Account: [Foo, Bar, 10001<A001>], [A001, Savings, 250.0, <[[Foo, Bar, 10001<A001>], [Bar, Foo, 10001<A001>]]>]
Hibernate: delete from ACCT_MEMBER_TBL where ACCT_ID=?
Hibernate: delete from ACCT_MEMBER_TBL where MEMBER_ID=?
Hibernate: delete from Member where memberId=?
Hibernate: delete from ACCT_MEMBER_TBL where MEMBER_ID=?
Hibernate: delete from Member where memberId=?
Hibernate: delete from ACCOUNT_TBL where ACCT_ID=?
DELETE <Member>: Member+Account: [A001, Savings, 250.0, <[[Foo, Bar, 10001<A001>], [Bar, Foo, 10001<A001>]]>]
Jun 16, 2012 10:49:13 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/mydb]

We have successfully demonstrated an example for bidirectional one-to-many bidirectional relationship using the XML mapping file approach in JPA.

We will not look at many-to-many relationship as it is similar to one-to-many bidirectional example explored above with a join table.

Once data is collected and stored in a relational database, enterprise applications need access to the data for various business analysis purposes. This means one needs to query the database to access that data. This is where the Java Persistence Query Language (JPQL) comes to the rescue.

We will now look at using Java Persistence Query Language (JPQL) with some examples.

Create a Java class called AccountMemberQuery01 that will query both the Account and Member objects using JPA:

Listing.26
package com.polarsparc.jpa;

import java.util.List;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public class AccountMemberQuery01 {
public static void main(String[] args) throws Exception {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Fetch all account IDs
{
String query = "SELECT a.accountId FROM Account02 a"; // [1]

Query qry = em.createQuery(query);

@SuppressWarnings("unchecked")
List<String> list = qry.getResultList();

System.out.printf("Query result size: %d\n", list.size());
System.out.printf("Account ID List:\n");
for (String id : list) {
System.out.printf("\tAccount ID: %s\n", id);
}
}

// Fetch all account IDs and account types
{
String query = "SELECT a.accountId, a.accountType FROM Account02 a"; // [2]

Query qry = em.createQuery(query);

@SuppressWarnings("unchecked")
List<Object[]> list = qry.getResultList();

System.out.printf("Query result size: %d\n", list.size());
System.out.printf("Account ID & Type List:\n");
for (Object[] res : list) {
System.out.printf("\tAccount ID: %s, Type: %s\n", res[0], res[1]);
}
}

// Query for account ID A001
{
String query = "SELECT a FROM Account02 a WHERE a.accountId = 'A001'"; // [3]

Query qry = em.createQuery(query);

@SuppressWarnings("unchecked")
List<Account02> list = qry.getResultList();
if (list.size() > 0) {
System.out.printf("Account: %s\n", list.get(0));
}
}

// Query for the count of member(s)
{
String query = "SELECT COUNT(m) FROM Member07 m"; // [4]

Query qry = em.createQuery(query);

Long count = (Long) qry.getSingleResult();
System.out.printf("Count of member(s): %d\n", count);
}

// Query for members of account ID A001
{
String query = "SELECT m FROM Account02 a JOIN a.members7 m WHERE
a.accountId = 'A001'"; // [5]

Query qry = em.createQuery(query);

@SuppressWarnings("unchecked")
List<Member07> list = qry.getResultList();

System.out.printf("Member List:\n");
for (Member07 m : list) {
System.out.printf("\tMember: %s %s\n", m.getFirstName(), m.getLastName());
}
}
}
}

When using JPQL, we do not query the underlying database tables instead we query using the entities, which in our case would be the Account and Member entities. In JPA, one can query for entities using an instance of the Query object. To obtain an instance of the Query object, invoke the createQuery() method on the EntityManager instance.

In // [1], we are querying for all the Account entities. Invoking the getResultList() method on the Query object executes the query and returns the result as a list. Since we are querying for all the Account entity IDs, the execution of the query will return a List<String>.

In // [2], we are querying for the accountId and accountType members of all Account entities. Since we have request for two members, the execution of the query will return a List<Object[]>.

In // [3], we are using a filter condition to fetch the Account entity for the accountId "A001". The execution of the query will return a List<Account>.

In // [4], we are using the aggregate function COUNT to count the number of Members associated with the Account entity. Invoking the getSingleResult() method on the Query object executes the query and returns the result as a single value.

In // [5], we are using an inner JOIN over the Account and Member entities for the accountId "A001" to fetch all the Member entities.

In order to test our example, let us create a script called bin/query01.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.AccountMemberQuery01

If we run the script bin/query01.sh, we see the following output:

Output.9

Jun 23, 2012 8:50:40 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 23, 2012 8:50:40 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 23, 2012 8:50:40 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 23, 2012 8:50:40 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 23, 2012 8:50:41 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 23, 2012 8:50:41 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 23, 2012 8:50:41 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 23, 2012 8:50:41 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 23, 2012 8:50:41 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=****, password=****, autocommit=true, release_mode=auto}
Jun 23, 2012 8:50:41 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 23, 2012 8:50:41 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 23, 2012 8:50:41 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select account02x0_.ACCT_ID as col_0_0_ from ACCOUNT_TBL account02x0_
Query result size: 2
Account ID List:
Account ID: A001
Account ID: A002
Hibernate: select account02x0_.ACCT_ID as col_0_0_, account02x0_.ACCT_TYPE as col_1_0_ from ACCOUNT_TBL account02x0_
Query result size: 2
Account ID & Type List:
Account ID: A001, Type: Savings
Account ID: A002, Type: Checking
Hibernate: select account02x0_.ACCT_ID as ACCT1_3_, account02x0_.ACCT_TYPE as ACCT2_3_, account02x0_.BALANCE as BALANCE3_ from ACCOUNT_TBL account02x0_ where account02x0_.ACCT_ID='A001'
Account: Hibernate: select members7x0_.ACCT_ID as ACCT1_3_2_, members7x0_.MEMBER_ID as MEMBER2_2_, member07x1_.memberId as memberId0_0_, member07x1_.firstName as firstName0_0_, member07x1_.lastName as lastName0_0_, member07x1_.zip as zip0_0_, member07x1_1_.ACCT_ID as ACCT1_2_0_, account02x2_.ACCT_ID as ACCT1_3_1_, account02x2_.ACCT_TYPE as ACCT2_3_1_, account02x2_.BALANCE as BALANCE3_1_ from ACCT_MEMBER_TBL members7x0_ inner join Member member07x1_ on members7x0_.MEMBER_ID=member07x1_.memberId left outer join ACCT_MEMBER_TBL member07x1_1_ on member07x1_.memberId=member07x1_1_.MEMBER_ID left outer join ACCOUNT_TBL account02x2_ on member07x1_1_.ACCT_ID=account02x2_.ACCT_ID where members7x0_.ACCT_ID=?
[A001, Savings, 250.0, <[[Alice, Girl, 10001<A001>], [Bob, Boy, 10001<A001>]]>]
Hibernate: select count(member07x0_.memberId) as col_0_0_ from Member member07x0_ left outer join ACCT_MEMBER_TBL member07x0_1_ on member07x0_.memberId=member07x0_1_.MEMBER_ID limit ?
Count of member(s): 3
Hibernate: select member07x2_.memberId as memberId0_, member07x2_.firstName as firstName0_, member07x2_.lastName as lastName0_, member07x2_.zip as zip0_, member07x2_1_.ACCT_ID as ACCT1_2_ from ACCOUNT_TBL account02x0_ inner join ACCT_MEMBER_TBL members7x1_ on account02x0_.ACCT_ID=members7x1_.ACCT_ID inner join Member member07x2_ on members7x1_.MEMBER_ID=member07x2_.memberId left outer join ACCT_MEMBER_TBL member07x2_1_ on member07x2_.memberId=member07x2_1_.MEMBER_ID where account02x0_.ACCT_ID='A001'
Member List:
Member: Alice Girl
Member: Bob Boy

We have successfully demonstrated an example on using some simple queries in JPQL.

Often times we encounter situation when we query with different input values. For these situations, we have two ways of passing input values to JPQL - Query using Positional Parameters or using Named Parameters.

With Positional Parameters, the query string will contain one or more parameter(s) that contain a question mark (?) followed by a number (starting at 1).

With Named Parameters, the query string will contain one or more parameter(s) that contain a colon (:) followed by a parameter name.

The following Java class called AccountMemberQuery02 will demonstrated the use of Positional Parameters and Named Parameters in queries:

Listing.27
package com.polarsparc.jpa;

import java.util.List;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public class AccountMemberQuery02 {
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.out.printf("Usage: java %s <account-id> <account-type>\n",
AccountMemberQuery02.class.getName());
System.exit(1);
}

EntityManagerFactory emf = Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Query using Positional Parameters for input
{
String query = "SELECT a FROM Account02 a WHERE a.accountId = ?1
AND a.accountType = ?2"; // [1]

Query qry = em.createQuery(query);
qry.setParameter(1, args[0]);
qry.setParameter(2, args[1]);

@SuppressWarnings("unchecked")
List<Account02> list = qry.getResultList();

System.out.printf("Query <Positional Parameters> result size: %d\n", list.size());
if (list.size() > 0) {
System.out.printf("Account Type & Balance:\n");
for (Account02 acct : list) {
System.out.printf("\tAccount Type: %s, Balance: %f\n",
acct.getAccountType(), acct.getBalance());
}
}
}

// Query using Named Parameters for input
{
String query = "SELECT a FROM Account02 a WHERE a.accountId = :id
AND a.accountType = :type"; // [2]

Query qry = em.createQuery(query);
qry.setParameter("id", args[0]);
qry.setParameter("type", args[1]);

@SuppressWarnings("unchecked")
List<Account02> list = qry.getResultList();

System.out.printf("Query <Named Parameters> result size: %d\n", list.size());
if (list.size() > 0) {
System.out.printf("Account Type & Balance:\n");
for (Account02 acct : list) {
System.out.printf("\tAccount Type: %s, Balance: %f\n",
acct.getAccountType(), acct.getBalance());
}
}
}
}
}

In // [1], we are using Positional Parameters. Notice the use of the parameters ?1 and ?2. The input values are specified by invoking the methods setParameter(1, args[0]) and setParameter(2, args[1]) respectively.

In // [2], we are using Named Parameters. Notice the use of the parameters :id and :type. The input values are specified by invoking the methods setParameter("id", args[0]) and setParameter("type", args[1]) respectively.

In order to test our example, let us create a script called bin/query02.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.AccountMemberQuery02 $1 $2

If we run the script bin/query02.sh with input A002 and Checking, we see the following output:

Output.10

Jun 23, 2012 10:30:35 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 23, 2012 10:30:35 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 23, 2012 10:30:35 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 23, 2012 10:30:35 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 23, 2012 10:30:36 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 23, 2012 10:30:36 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 23, 2012 10:30:36 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 23, 2012 10:30:36 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 23, 2012 10:30:36 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=dbuser, password=****, autocommit=true, release_mode=auto}
Jun 23, 2012 10:30:36 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 23, 2012 10:30:36 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 23, 2012 10:30:36 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select account02x0_.ACCT_ID as ACCT1_1_, account02x0_.ACCT_TYPE as ACCT2_1_, account02x0_.BALANCE as BALANCE1_ from ACCOUNT_TBL account02x0_ where account02x0_.ACCT_ID=? and account02x0_.ACCT_TYPE=?
Query <Positional Parameters> result size: 1
Account Type & Balance:
Account Type: Checking, Balance: 375.000000
Hibernate: select account02x0_.ACCT_ID as ACCT1_1_, account02x0_.ACCT_TYPE as ACCT2_1_, account02x0_.BALANCE as BALANCE1_ from ACCOUNT_TBL account02x0_ where account02x0_.ACCT_ID=? and account02x0_.ACCT_TYPE=?
Query <Named Parameters> result size: 1
Account Type & Balance:
Account Type: Checking, Balance: 375.000000

We have successfully demonstrated an example on using input parameters with queries in JPQL.

In the last two examples we executed queries in JPQL using the Query interface that is created by calling the createQuery() method on the EntityManager. Using this approach is inefficient as the query string has to be parsed for each execution at runtime. Also, as the application grows in complexity, the query strings could be scattered all over the place in the code that would make it hard to read and maintain.

Enter Named Query. A Named Query is a static query that is defined once either in the mapping file META-INF/orm.xml or in the appropriate Entity classes. Named Queries are parsed and precompiled once at startup and result in very efficient query execution.

Let us start with the XML mapping file (META-INF/orm.xml) approach.

We need to modify the object-relational mapping file META-INF/orm.xml to define the Named Queries used in our example. The following is our META-INF/orm.xml file:

Listing.28
<?xml version="1.0" encoding="UTF-8" ?>

<entity-mappings version="2.0"
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
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

<named-query name="Account.findAccountByIdAndType">
<query><![CDATA[
SELECT a FROM Account01 a WHERE a.accountId = :id AND a.accountType = :type
]]></query>
</named-query>

<entity class="com.polarsparc.jpa.Account01" name="Account01">
<table name="ACCOUNT_TBL"/>

<attributes>
<id name="accountId">
<column name="ACCT_ID" />
</id>

<basic name="accountType">
<column name="ACCT_TYPE" />
</basic>

<basic name="balance">
<column name="BALANCE" />
</basic>

<one-to-many name="members6" mapped-by="account1">
<cascade>
<cascade-all />
</cascade>
</one-to-many>
</attributes>
</entity>

<entity class="com.polarsparc.jpa.Member06" name="Member06">
<table name="Member"/>

<named-query name="Member.findMemberByLastName">
<query><![CDATA[
SELECT m FROM Member06 m WHERE m.lastName = :name
]]></query>
</named-query>

<attributes>
<id name="memberId">
<column name="memberId" />
<generated-value strategy="AUTO" />
</id>

<basic name="firstName">
<column name="firstName" />
</basic>

<basic name="lastName">
<column name="lastName" />
</basic>

<basic name="zip">
<column name="zip" />
</basic>

<many-to-one name="account1">
<join-table name="ACCT_MEMBER_TBL">
<join-column name="MEMBER_ID" />
<inverse-join-column name="ACCT_ID" />
</join-table>
<cascade>
<cascade-all />
</cascade>
</many-to-one>
</attributes>
</entity>

</entity-mappings>

The <named-query> element allows us to define a Named Query. It contains a "name" attribute and a <query> element. The static query string is defined inside the <query> element and the "name" attribute associates a name with the query string. The convention used to name queries is the form "Entity.Name". The <named-query> element can appear as a sub-element under <entity-mappings> element or as a sub-element under <entity> element. In our example, we have defined two named queries: "Account.findAccountByIdAndType" under <entity-mappings> and "Member.findMemberByLastName" under Member06 <entity>.

The following Java class called AccountMemberQuery03 will demonstrated the use of Named Query using the XML mapping file (META-INF/orm.xml) approach:

Listing.29
package com.polarsparc.jpa;

import java.util.List;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public class AccountMemberQuery03 {
public static void main(String[] args) throws Exception {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Query using findMemberByLastName
{
Query qry = em.createNamedQuery("Member.findMemberByLastName");
qry.setParameter("name", "Girl");

@SuppressWarnings("unchecked")
List<Member06> list = qry.getResultList();
if (list.size() > 0) {
System.out.printf("Member Names:\n");
for (Member06 mem : list) {
System.out.printf("\tMember Name: %s %s\n", mem.getFirstName(),
mem.getLastName());
}
}
}

// Query using findAccountByIdAndType
{
Query qry = em.createNamedQuery("Account.findAccountByIdAndType");
qry.setParameter("id", "A001");
qry.setParameter("type", "Savings");

@SuppressWarnings("unchecked")
List<Account01> list = qry.getResultList();
if (list.size() > 0) {
System.out.printf("Account Type & Balance:\n");
for (Account01 acct : list) {
System.out.printf("\tAccount Type: %s, Balance: %f\n",
acct.getAccountType(), acct.getBalance());
}
}
}
}
}

In order to use Named Query in JPQL, we still need an instance of the Query object. However, to obtain an instance of the Query object for the Named Query, invoke the createNamedQuery() method on the EntityManager instance specifiying the query name. The query name has to match the name specified in the XML mapping file (META-INF/orm.xml). For our example, we use "Account.findAccountByIdAndType" and "Member.findMemberByLastName".

In order to test our example, let us create a script called bin/query03.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.AccountMemberQuery03

If we run the script bin/query03.sh, we see the following output:

Output.11

Jun 24, 2012 4:08:54 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 24, 2012 4:08:54 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 24, 2012 4:08:54 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 24, 2012 4:08:54 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 24, 2012 4:08:55 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 24, 2012 4:08:55 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 24, 2012 4:08:55 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 24, 2012 4:08:55 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 24, 2012 4:08:55 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=dbuser, password=****, autocommit=true, release_mode=auto}
Jun 24, 2012 4:08:55 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 24, 2012 4:08:55 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 24, 2012 4:08:55 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select member06x0_.memberId as memberId0_, member06x0_.firstName as firstName0_, member06x0_.lastName as lastName0_, member06x0_.zip as zip0_, member06x0_1_.ACCT_ID as ACCT1_3_ from Member member06x0_ left outer join ACCT_MEMBER_TBL member06x0_1_ on member06x0_.memberId=member06x0_1_.MEMBER_ID where member06x0_.lastName=?
Hibernate: select account01x0_.ACCT_ID as ACCT1_2_0_, account01x0_.ACCT_TYPE as ACCT2_2_0_, account01x0_.BALANCE as BALANCE2_0_ from ACCOUNT_TBL account01x0_ where account01x0_.ACCT_ID=?
Hibernate: select account01x0_.ACCT_ID as ACCT1_2_0_, account01x0_.ACCT_TYPE as ACCT2_2_0_, account01x0_.BALANCE as BALANCE2_0_ from ACCOUNT_TBL account01x0_ where account01x0_.ACCT_ID=?
Member Names:
Member Name: Alice Girl
Member Name: Eve Girl
Hibernate: select account01x0_.ACCT_ID as ACCT1_2_, account01x0_.ACCT_TYPE as ACCT2_2_, account01x0_.BALANCE as BALANCE2_ from ACCOUNT_TBL account01x0_ where account01x0_.ACCT_ID=? and account01x0_.ACCT_TYPE=?
Account Type & Balance:
Account Type: Savings, Balance: 250.000000

We have successfully demonstrated an example on using Named Query in JPQL with the XML mapping file (META-INF/orm.xml) approach.

Now, let us demonstrate Named Query in JPQL using the JPA Annotations approach.

Modify the Account02 class in Java as follows:

Listing.30
package com.polarsparc.jpa;

import java.util.*;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.OneToMany;
import javax.persistence.CascadeType;
import javax.persistence.NamedQuery;

@Entity
@Table(name="ACCOUNT_TBL")
@NamedQuery(name="Account02.findAccountByIdAndType",
query="SELECT a FROM Account02 a WHERE a.accountId = :id AND a.accountType = :type")
public class Account02 {
@Id
@Column(name="ACCT_ID")
private String accountId;

@Column(name="ACCT_TYPE")
private String accountType;

@Column(name="BALANCE")
private double balance;

@OneToMany(cascade={CascadeType.ALL}, mappedBy="account2")
private Collection<Member07> members7 = new ArrayList<Member07>();

public String getAccountId() {
return accountId;
}

public void setAccountId(String accountId) {
this.accountId = accountId;
}

public String getAccountType() {
return accountType;
}

public void setAccountType(String accountType) {
this.accountType = accountType;
}

public double getBalance() {
return balance;
}

public void setBalance(double balance) {
this.balance = balance;
}

public Collection<Member07> getMembers7() {
return members7;
}

public void setMembers6(Collection<Member07> members) {
this.members7 = members;
}

@Override
public String toString() {
return "[" + accountId + ", " + accountType + ", " + balance + ", <" + members7 + ">]";
}
}

The @NamedQuery annotation specifies the Named Query. It contains name and query attributes. The static query string is assigned as the value of the query attribute and the name attribute associates a name with the query string.

Also, modify the Member07 class in Java as follows:

Listing.31
package com.polarsparc.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Table;
import javax.persistence.ManyToOne;
import javax.persistence.CascadeType;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQuery;

@Entity
@Table(name="Member")
@NamedQuery(name="Member07.findMemberByLastName",
query="SELECT m FROM Member07 m WHERE m.lastName = :name")
public class Member07 {
@Id
@GeneratedValue
@Column(name="memberId")
private long memberId;

@Column(name="firstName")
private String firstName;

@Column(name="lastName")
private String lastName;

@Column(name="zip")
private String zip;

@ManyToOne(cascade={CascadeType.ALL})
@JoinTable(name="ACCT_MEMBER_TBL",
joinColumns={@JoinColumn(name="MEMBER_ID")},
inverseJoinColumns={@JoinColumn(name="ACCT_ID")})
private Account02 account2;

public long getMemberId() {
return memberId;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

public Account02 getAccount2() {
return account2;
}

public void setAccount2(Account02 account) {
this.account2 = account;
}

@Override
public String toString() {
return "[" + firstName + ", " + lastName + ", " + zip + "<" +
account2.getAccountId() + ">]";
}
}

The following Java class called AccountMemberQuery04 will demonstrated the use of Named Query using the JPA Annotations approach:

Listing.32
package com.polarsparc.jpa;

import java.util.List;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.EntityManager;
import javax.persistence.Query;

public class AccountMemberQuery04 {
public static void main(String[] args) throws Exception {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PolarSparcJPA");
EntityManager em = emf.createEntityManager();

// Query using findMemberByLastName
{
Query qry = em.createNamedQuery("Member07.findMemberByLastName");
qry.setParameter("name", "Girl");

@SuppressWarnings("unchecked")
List<Member07> list = qry.getResultList();
if (list.size() > 0) {
System.out.printf("Member Names:\n");
for (Member07 mem : list) {
System.out.printf("\tMember Name: %s %s\n", mem.getFirstName(),
mem.getLastName());
}
}
}

// Query using findAccountByIdAndType
{
Query qry = em.createNamedQuery("Account02.findAccountByIdAndType");
qry.setParameter("id", "A001");
qry.setParameter("type", "Savings");

@SuppressWarnings("unchecked")
List<Account02> list = qry.getResultList();
if (list.size() > 0) {
System.out.printf("Account Type & Balance:\n");
for (Account02 acct : list) {
System.out.printf("\tAccount Type: %s, Balance: %f\n",
acct.getAccountType(), acct.getBalance());
}
}
}
}
}

Notice the use of query names "Member07.findMemberByLastName" and "Account02.findAccountByIdAndType". The query names have to match the names specified in the name attribute of the @NamedQuery annotation.

In order to test our example, let us create a script called bin/query04.sh as follows:

CP=.:build/classes:./META-INF

for jar in $HOME/Products/hibernate-4.1.3/lib/required/*.jar
           $HOME/Products/hibernate-4.1.3/lib/jpa/*.jar
           $HOME/Products/mysql-connector-java-5.1.20/*.jar
do
    CP=$CP:$jar
done

java -cp $CP com.polarsparc.jpa.AccountMemberQuery04

If we run the script bin/query04.sh, we see the following output:

Output.12

Jun 24, 2012 5:23:42 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jun 24, 2012 5:23:42 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.3.Final}
Jun 24, 2012 5:23:42 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jun 24, 2012 5:23:42 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jun 24, 2012 5:23:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
Jun 24, 2012 5:23:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20
Jun 24, 2012 5:23:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000006: Autocommit mode: true
Jun 24, 2012 5:23:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/mydb]
Jun 24, 2012 5:23:42 PM org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000046: Connection properties: {user=dbuser, password=****, autocommit=true, release_mode=auto}
Jun 24, 2012 5:23:43 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Jun 24, 2012 5:23:43 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
Jun 24, 2012 5:23:43 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select member07x0_.memberId as memberId0_, member07x0_.firstName as firstName0_, member07x0_.lastName as lastName0_, member07x0_.zip as zip0_, member07x0_1_.ACCT_ID as ACCT1_2_ from Member member07x0_ left outer join ACCT_MEMBER_TBL member07x0_1_ on member07x0_.memberId=member07x0_1_.MEMBER_ID where member07x0_.lastName=?
Hibernate: select account02x0_.ACCT_ID as ACCT1_1_0_, account02x0_.ACCT_TYPE as ACCT2_1_0_, account02x0_.BALANCE as BALANCE1_0_ from ACCOUNT_TBL account02x0_ where account02x0_.ACCT_ID=?
Hibernate: select account02x0_.ACCT_ID as ACCT1_1_0_, account02x0_.ACCT_TYPE as ACCT2_1_0_, account02x0_.BALANCE as BALANCE1_0_ from ACCOUNT_TBL account02x0_ where account02x0_.ACCT_ID=?
Member Names:
Member Name: Alice Girl
Member Name: Eve Girl
Hibernate: select account02x0_.ACCT_ID as ACCT1_1_, account02x0_.ACCT_TYPE as ACCT2_1_, account02x0_.BALANCE as BALANCE1_ from ACCOUNT_TBL account02x0_ where account02x0_.ACCT_ID=? and account02x0_.ACCT_TYPE=?
Account Type & Balance:
Account Type: Savings, Balance: 250.000000

We have successfully demonstrated an example on using Named Query in JPQL with the JPA Annotations approach.

With this we conclude the Quick Introduction to Java Persistence API (JPA).