JBay Solutions Development Blog on Java, Android, Play2 and others
RSS RSS RSS RSS

Getting Started with JPA 2 Tutorial

So, one of the most common comments I get to hear while at work almost every week regarding JPA is : "Oh I Just hate JPA!" . I never really had much of a problem with it, obviously sometimes with JPA something just doesn't want to work for some strange reason and the Logs don't really help finding the problem but most of the days everything works fine.

At some point I decided that I would write a few tutorials on JPA. Some time has passed since and now I'm actually doing it. Use the comments section for corrections if you find any problems or errors, or if you would just like to leave a kind word!

This is the first tutorial of (what I hope) a series of tutorials that focus on different parts of JPA, Entity Beans and related stuff. A few assumptions are made, like basic knowledge of SQL and relational databases, what an EJB is and where does it run, Unit Testing, an IDE is setup (like NetBeans) , a MySQL database is setup and basic knowledge on how to use it, and stuff like that.

This tutorial will serve as the setup for the following tutorials. It will provide background knowledge of what JPA is, what it does, how to create a basic Entity Beans to interact with the database, how to test these Beans, etc.

Simply understanding some basic stuff that is explained here, such as Entity Lifecycle and Entity Manager contexts, will save a developer a lot of work when trying to write something more advanced!

On the following tutorials more advanced topics such as relationships will be covered.

For this tutorial EclipseLink, JUnit and MySQL are needed, so if you are missing any of these this is the time to go and get them:

I'll also be using MySQL WorkBench to visually create the database structure, the IDE used is irrelevant since the tutorial will be more focused on the source code and basic knowledge side of things.

I also created a NetBeans Project with all the source code used on this Tutorial. You can Download it Here but to properly use it you'll need to place the  EclipseLink Jars and MySQL connector Jar and Persistence Jar inside the correct Lib folder inside the project. You'll also have to add these libraries to the Testing Libraries of the project.


The Database

So, lets start the show! The name of the schema to be used in MySQL is "mydb", but if you decide to use a different one just remember to change any reference to mydb in this tutorial to the name of your Schema. First let us start by creating the following table in MySQL:

The SQL for that table is like so :

CREATE TABLE IF NOT EXISTS `mydb`.`company` (
`idcompany` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NOT NULL ,
`country` VARCHAR(45) NOT NULL ,
`privat` TINYINT(1) NOT NULL DEFAULT 0 ,
`ownername` VARCHAR(45) NULL ,
`numberemployees` INT NOT NULL DEFAULT 0 ,
`income` DOUBLE NOT NULL DEFAULT 0.0 ,
PRIMARY KEY (`idcompany`) )
ENGINE = InnoDB;`</blockquote>

This table has and ID column, which is auto incremental, a  few columns that are basic text, a column called privat that is a boolean and some ints and doubles.

The Persistence Unit

Now inside the IDE configure a new project to use J2EE Persistence (JPA) and Enterprise JavaBeans. Inside the META-INF folder there should be a file called persistence.xml, if there is none then create one. It should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="testPU" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="j**avax.persistence.jdbc.driver**" value="com.mysql.jdbc.Driver"/>
            <property name="**javax.persistence.jdbc.url**" value="jdbc:mysql://localhost:3306/mydb"/>
            <property name="**javax.persistence.jdbc.user**" value="root"/>
            <property name="**javax.persistence.jdbc.password**" value="root"/>
        </properties>
    </persistence-unit>
</persistence>

The persistence.xml file indicates that there is only one Persistence Unit, which we called testPU, the transaction type for this PU is RESOURCE_LOCAL. There are two valid types of transaction:

  • JTA

  • RESOURCE_LOCAL

What these options mean is: If one selects RESOURCELOCAL then the transactions will be managed by the JPA Provider Implementation in use. If JTA is specified then the transactions will be managed by the Application Server the application will run in. Another way of looking at this is that if one only wants to have JPA transactions then RESOURCELOCAL is a good choice. If one would like the transactions to contain other resources other than JPA, like EJBs, JCA or JMS then JTA is the correct choice.

When defining a Persistence Unit a list of Entity Beans to be managed can also be included, or one can simply do : false, which will make all Entity Beans in the archive managed by the PU.

The remaing properties in the PU are pretty straight forward to understand. Make them the configurations of your MySQL database and Schema. Other properties for EclipseLink can be found here: Properties JPA2

The Entity Bean

An Entity Bean is a "lightweight persistent domain object" (JPA Sepc) , what this means is that an Entity object represents a set of attributes of an entry on the database.  It allows a developer to create, access and modify entries on a Database in an Object Oriented fashion like if they were simple POJOs.

The Entity Bean is therefore a Java Object that simply has to follow a few rules that are not that difficult. Our Entity Bean will be called CompanyEntity.Java , and it was created inside the package : com.jbaysolutions.testing.entity . Feel free to change it to a different package.

I've split the file in several sections here so that I can go through and explain what is going in each section. The complete file can be found here.

The first part of the file goes like so :

 package com.jbaysolutions.testing.entity;
 import javax.persistence.*;
 import java.io.Serializable;

 @Entity
 @Table(name = "company")
 public class CompanyEntity implements Serializable {

@Entity

A Java object, to be considered an Entity Bean must have the Entity annotation ( @Entity ) on the class or be denoted on the ejb-jar.xml file. In our example we use the @Entity annotation.  This annotation and the remaining ones that characterise an Entity Bean can be found inside the javax.persistence package.

Also, an Entity Bean must be a top level class, it must not be final, nor the persistent elements that it contains.

@Table

The @Table annotation is used to specify which table, from which catalogue and which schema this Entity Bean is representing. In this case we simply use the name attribute to define the table name.

The Entity Bean can also be Serializable, which is very handy when there is the need to pass this object somewhere else, like for example as the return of a web service method or EJB.

The next bit of the class looks like this :

     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Column(name = "idcompany")
     private Integer idcompany;

@Id

In this section an attribute called idcompany is defined as an Integer object which is the Primary Key of the created table. We use the @Id annotation to identify this attribute as the ID of the Entity Bean. An Entity Bean MUST have an Id, which can be a single attribute or several attributes ( composite primary key). The Id of the Entity Bea MUST be serializable.

So this ID will be used by Entity Manager to uniquely identify the entity in its persistence context. It is also a part of the JPA spec that an Application MUST NOT modify the value of the Primary Key or any of the values of a Composite Primary Key. It states that the behaviour is undefined if this occurs, which basically means that it might work but if the JPA provider is changed then the behaviour might not be the same across all Providers and therefore the application behaviour might change. In other words: Don't do it!

Composite Primary Keys will be explained in a later tutorial.

@GeneratedValue

This annotation provides the strategy to be used for the generation of the Id attributes of the Entity Bean. It is implementation specific and its behaviour is not defined on the specification. There are 4 possible choices :

  • auto

  • identity

  • table

  • sequence

For this example we are using IDENTITY due to the fact that MySQL supports identity columns. If we were using Oracle which supports Sequence objects, we would be using the SEQUENCE choice and would define which sequence was to be used for the generation of the Ids. The TABLE option can be used in any kind of database and means that JPA will use another table for the Ids to be used, the new table will have a column of the same type the Id has and each row will correspond to a specific Id on the original table.

Option AUTO will make JPA decide itself what is the best strategy to use, in case of EclipseLink the default choice will be TABLE.

@Column

The @Column annotation informs JPA which column this attribute corresponds to and other constraints of the attribute such has length and precision. If the name of the column is the same as the name of the attribute then there is no need to specify the "name" property of the annotation.

The next section of code is the definition of the remaining columns:

     @Column(name = "name")
     @Basic(optional = false)
     private String name;

     @Column(name = "country")
     @Basic(optional = false)
     private String country;

     @Column(name = "privat")
     @Basic(optional = false)
     private Boolean privat = false;

     @Column(name = "ownername")
     @Basic(optional = true)
     private String ownerName;

     @Column(name = "numberemployees")
     @Basic(optional = false)
     private int numberEmployees = 0;

     @Column(name = "income")
     @Basic(optional = false)
     private double income = 0.0;

@Basic

The Basic annotation can be applied to any persistent attribute of the Entity Bean as long as it is a primitive, a primitive wrapper or Serializable object. It is optional and cannot be used for Id attributes. Through the use of @Basic annotation we can specify the type of fetch to be used and whether the attribute is optional or not.

The next section of code is concerning the Constructors of the Entity Bean:

     public CompanyEntity() {
     }

     public CompanyEntity(String name,
                          String country,
                          Boolean privat,
                          String ownerName,
                          int numberEmployees,
                          double income) {
         this.name = name;
         this.country = country;
         this.privat = privat;
         this.ownerName = ownerName;
         this.numberEmployees = numberEmployees;
         this.income = income;
     }

Entity Bean Constructors

According to the JPA Specification the Entity Bean must have a no-args constructor defined. It can have many other constructors, but the no-args must be defined and must be either Public or Protected. In our example we have two, for no particular reason.

The remaining definition of the Entity Bean is basically following the Javabeans conventions for the getter and setter methods for the attributes.


Testing the Entity Bean

Usually all operations regarding the Entity Beans would be performed inside some EJB that would take care of all the business logic and persistence related parts. That will be covered in the next tutorial. In this one we'll be using JUnit testing, which is a good practice, specially for later once this exercise gets expanded to make use of EJBs.

So by using a JUnit test class all operations can be simulated with the Entity Bean that was just created. The JUnit class that we'll use can be found here and I'll go through it section by section and explain what is happening.

 public class CompanyEntityTest {

     private static EntityManager em = null;

     @BeforeClass
     public static void setUpClass() throws Exception {
         if (em == null) {
             em = (EntityManager) Persistence.
                       createEntityManagerFactory("testPU").
                       createEntityManager();
         }
     }

This first section of the JUnit basically performs the setup of the Entity Manager that will be used for the remaining parts of the Class.  The @BeforeClass annotation basically means that this method will be run once before any of the test methods of the Class. The test methods will be marked with the @Test annotation, as you will see next. More information regarding @BeforeClass can be found Here and it is beyond the scope of this tutorial.

Inside this method the Entity Manager object is created using a Factory. This factory takes as an argument the name of the Persistence Unit that is to be used. Since we only have one created on the persistence.xml file, we'll use that one.

 @Test
 public void testAllOps(){
     // Start a transaction
     em.getTransaction().begin();

     // ------------  Create a Company C1 ---------
     CompanyEntity c1 = new CompanyEntity();
     c1.setCountry("Portugal");
     c1.setIncome(1000000.00);
     c1.setName("JBay");
     c1.setNumberEmployees(200);
     c1.setOwnerName("The Dude");
     c1.setPrivat(true);
     // At this Point the Entity does not have a
     // Persistent Identity and is not associated
     // with a persistent Context
     em.persist(c1); // Persist the Entity
     em.flush();
     // At this point the Entity has a Persistent
     // Identity and is associated with a Persistent
     // context.

     // ------------  Create a Company C2 ---------
     CompanyEntity c2 = new CompanyEntity();
     c2.setCountry("US");
     c2.setIncome(1000000000.00);
     c2.setName("Oracle");
     c2.setNumberEmployees(9000);
     c2.setOwnerName("Who?");
     c2.setPrivat(true);
     em.persist(c2);
     em.flush();

     System.out.println("Company 1 Id : " + c1.getIdcompany());
     System.out.println("Company 2 Id : " + c2.getIdcompany());

The method testAllOps is the one that will be used to test all the operations. It is marked with @Test which means that it will get executed after @BeforeClass which then means that the EntityManager object "em" is already instantiated and ready for usage. Because the Persistence Unit that we are using is using transaction type RESOURCE_LOCAL we begin a transaction first of all, if it was of the type JTA it would throw an exception if we tried to manage the transaction like that.

At this point we should look at the Entity Bean Lifecycle.

Entity Lifecycle - New Entity

We then create a new Instance of the Entity Bean CompanyEntity which we call "c1".  Because when we create this new object c1 it does not contain an identity this Entity Bean is said to be "new".  Object c1 is then given a set of values for its attributes except for the Primary Key attribute.

Entity Lifecycle - Managed Entity

The Entity Manager "em" is then used to persist this object on the database. The method to be used is persist(T). If the method merge(T) would be used it would be incorrect because this method is used for update operations and not create operations. Calling the persist() method makes this Entity Bean c1 "managed" and associated with this persistence context.

The method * flush()* is called simply to synchronise the data of the Entity Bean with the database system. According to the JPA Spec the persistence provider is allowed to sync to the database system at other times other than when flush() is called but calling it ensures that the synchronisation happened at this point.

Entity Lifecycle - Removed Entity

The Entity Manager has a method called remove(T), this method is used to remove the entry related to the Id of that entity from the underlining database system. At this point, after the remove method was called on a Managed Entity Bean, that Entity Bean is said to be "removed".

The state of the object itself is exactly the same as before the remove method was called, with the difference that that entity no longer is associated with the persistence context of the Entity Manager and does not represent any entry on the database.

Entity Lifecycle - Detached Entity

A Detached Entity is an Entity that is not associated with a persistence context. It is a necessary thing for several operations and can happen in several ways, the most common ones being from detaching the entity from the persistence context and from serializing an entity or otherwise passing an entity by value.

To detach an Entity from a persistence context the method  detach() can be used on that Entity. Before doing so one must flush() the entity to ensure that changes are persisted. Associated Entities with the detached Entity are also detached is the relationship is marked with Cascade=DETACH or Cascade=ALL. The Cascade attribute will be addressed on a later tutorial when relationships between entities are covered.

If an Entity is new or already detached invoking detach() with produce no result.

There are several other nuances with Detached entities, specially regarding merging of a detached entity onto an existing persistence context. These are addressed on a later tutorial.

The next section of the JUnit file shows two searchs being performed on the persistence context:

 Query query = em.createQuery("Select c from CompanyEntity c where c.idcompany=:companyid");
 query.setParameter("companyid", c1.getIdcompany());
 CompanyEntity retrieved1 = (CompanyEntity) query.getSingleResult();
 assertSame(c1, retrieved1);

 query.setParameter("companyid", c2.getIdcompany());
 CompanyEntity retrieved2 = (CompanyEntity) query.getSingleResult();
 assertSame(c2,retrieved2);

 assertNotSame(c1,c2);
 assertNotSame(retrieved1,retrieved2);

The Query object on the above example is created using JPQL. There are other ways to create queries such as using a named query, which is a query that is defined on an Entity Bean and also queries can be created using native SQL language. In the example we use JPQL which stands for JPA Query Language, which is considered to be an Object oriented version on SQL. The example query has a Parameter called  :companyid which is set using the setParameter() method of the Query object.

The getSingleResult() method returns the unique object that matches the query. If no result is found a NoResultException is thrown. This exception does not mark the transaction for rollback. If more than one result matches the query then a NonUniqueResultException is thrown. This exception also does not mark the transaction for rollback.

Using the assertSame() method of the JUnit framework we verify that C1 and Retrieved1 are the same and also that C2 and Retrieved2 are the same. It is of crucial importance to understand this! According to the JPA2 Spec:

An EntityManager instance is associated with a persistence context. A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance

What this means is that there is only one copy of an Entity Bean object for each Identity inside a persistence context. So, inside a persistence context if we were to call the getSingleResult() method several times and assign its result to different variables, these would all be references to the exact same object and not different copies of similar objects.

One could test this by simply modifying the CompanyEntity object with :

 @Transient
 int random = new Random().nextInt();

 public int getRandom() {
     return random;
 }

This will generate a random integer for each Entity Object that gets created. Now, performing getSingleResult() for the same query with the same parameters on the JUnit test class assigning the result to different variables and calling getRandom() on all of those references will return the same int value, which shows that the Object is the same or that we got extremely lucky (not likely... but try it a few time to be sure).  This test could also be done by calling the hashCode() method of each object obviously and verifying that they all have the same hashcode but that would prevent the introduction of the @Transient annotation.

@Transient

The @Transient annotation is similar to the transient keyword in Java, in the sense that transient keyword means that an attribute of a class is not to be serializable, the @Transient annotation in JPA means that the Attribute is not to be persisted. Attributes annotated with @Transient are ignored by the Entity Manager and are not persisted on the Database system.

The remaining of the JUnit test class demonstrates the remaining operations already mentioned on this tutorial, such as updating of entries:

 c2.setOwnerName("No One");
 c2.setPrivat(false);
 em.merge(c2);
 em.flush();

 System.out.println("Company 2 Id : " + c2.getIdcompany());
 System.out.println("Company 2 Name : " + c2.getName());
 System.out.println("Company 2 Owner : " + c2.getOwnerName());
 System.out.println("Company 2 Private : " + retrieved2.getPrivat());

And also deleting of entries:

 em.remove(c1);
 em.remove(c2);
 // Both c1 c2 (and obviously retrieved1 and retrieved2) are removed,
 // which will happen upon commit of the Transaction
 em.getTransaction().commit();

References

Java Persistence Api 2 Specification

EclipseLink Properties



comments powered by Disqus