In this part of the series I will show some basic functionalities of Eclipse JPA as we create a very simple project.
Creating an entity class
It is easy to create an entity class. Right click on a package, New -> Entity.

Note: In our scenario we are not using a legacy database, so we will let Hibernate create all tables in the database for our entities.
Now you can specify the entity name:

You can add properties:

Access type
- Field - annotate the field itself (e.g. with @Id)
- Property - annotate the getter
You can also provide an alternative name for the table (other than the one derived from the class name):

It's better to use the property one.
Let's see the generated code.
package me.m1key.dogs.dao;
import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import javax.persistence.*;
/**
* Entity implementation class for Entity: Dog
*
*/
@Entity
@Table(name = "DOGS")
public class Dog implements Serializable {
private Long id;
private String name;
private static final long serialVersionUID = 1L;
public Dog() {
super();
}
@Id
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
//... Omitted for brevity.
}
You have a no-argument constructor (that's a JPA requirement). You have an ID annotated with @Id, the class is annotated with @Entity and you have the @Table annotation so that an alternative table name can be specified.
Note that in the peristence.xml file content was created automatically. It should look more or less like this.
<?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="Dogs">
<class>me.m1key.dogs.entities.Dog</class>
</persistence-unit>
</persistence>
Eclipse JPA validation
At this moment, if connected to your HSQLDB server, you might get the following error:
Table "DOGS" cannot be resolved

That's right, the table does not exist because we are not using a legacy database. We should let Hibernate create this table for us.
What is missing?
We specified the ID column, but we did not specify how to get its value. Use the @GeneratedValue annotation.
import javax.persistence.GeneratedValue;
// ...
@Id
@GeneratedValue
public Long getId() {
return this.id;
}
// ...
Compared to the project from the first part of the tutorial, you also need to add a dependency to slf4j-simple-1.5.8.jar.
One more thing we must add is database specific stuff in persistence.xml. Eclipse knows how to connect to the database, but your project doesn't.
...
<persistence-unit name="Dogs">
<class>me.m1key.dogs.entities.Dog</class>
<properties>
<property name="hibernate.archive.autodetecion" value="class, hbm" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
<property name="hibernate.connection.url" value="jdbc:hsqldb:hsql://localhost/dogsdb" />
<property name="hibernate.connection.username" value="sa" />
<property name="hibernate.c3p0.min_size" value="5" />
<property name="hibernate.c3p0.max_size" value="20" />
<property name="hibernate.c3p0.timeout" value="300" />
<property name="hibernate.c3p0.max_statements" value="50" />
<property name="hibernate.c3p0.idle_test_period" value="3000" />
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
</persistence-unit>
...
The hibernate.hbm2ddl.auto paramater is set to create because it is more convenient for us in this situation. It means that the database is dropped and recreated on application start-up, but is not dropped after it shuts down, so that you can investigate the content of it after the application has run.
DAO
Let's create simple DAO classes, namely a generic DAO and DogDAO.
package me.m1key.dogs.dao;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* Generic DAO for other DAOs to extend.
*
* @author Michal
*
*/
public abstract class GenericDAO {
private static EntityManagerFactory emf = Persistence
.createEntityManagerFactory("Dogs");
public EntityManager createEntityManager() {
return emf.createEntityManager();
}
public static void closeEntityManager() {
emf.close();
}
}
This is a simple generic DAO. All it does is it allows for using its createEntityManager method from extending classes. This is temporary design, not a recommended way of doing it, mind you.Now, look at the createEntityManagerFactory method - it specifies the persistence unit name. It is the very same name which is specified in our persistence.xml file.
Let's create the DogDAO then.
package me.m1key.dogs.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import me.m1key.dogs.entities.Dog;
/**
* Dog DAO.
*
* @author MichalH
*
*/
public class DogDAO extends GenericDAO {
public void persistDog(Dog dog) {
EntityManager em = createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(dog);
tx.commit();
em.close();
}
@SuppressWarnings("unchecked")
public List getAllDogs() {
EntityManager em = createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Query allDogsQuery = em.createQuery("select d from Dog d");
List allDogs = allDogsQuery.getResultList();
tx.commit();
em.close();
return allDogs;
}
}
Two simple methods, one for saving dogs in the database, and one for retrieving all of them.
Let it run
Below, sample code that uses those DAO classes.
package me.m1key.dogs;
import java.util.ArrayList;
import java.util.List;
import me.m1key.dogs.dao.DogDAO;
import me.m1key.dogs.dao.GenericDAO;
import me.m1key.dogs.entities.Dog;
/**
* Launches the simple JPA demonstration application.
*
* @author MichalH
*
*/
public class Launcher {
/**
* @param args
*/
public static void main(String[] args) {
DogDAO dogDao = new DogDAO();
Dog michalsDog = new Dog();
michalsDog.setName("Sega");
Dog someOtherDog = new Dog();
// http://www.fundognames.com/male-dog-names-p-9.html
someOtherDog.setName("Assan");
try {
dogDao.persistDog(michalsDog);
} catch (Exception e) {
System.err.println("Exception while saving dog: " + michalsDog);
System.err.println(e);
e.printStackTrace();
}
try {
dogDao.persistDog(someOtherDog);
} catch (Exception e) {
System.err.println("Exception while saving dog: " + someOtherDog);
System.err.println(e);
e.printStackTrace();
}
List<Dog> allDogs = dogDao.getAllDogs();
for (Dog doggie : allDogs) {
System.out.println(doggie);
}
GenericDAO.closeEntityManager();
}
}
It's very very basic at this point. But what happens if you run it?
[1, Sega] [2, Assan]Alright! Something got saved in the database and was successfully retrieved. Note that each persisting operation and retrieval occur in separate transactions. If you don't like that, there are several solutions available:
- Create business classes instead of DAO classes that handle persistence AND business logic
- Use sf.getCurrentSession().beginTransaction() where sf is session factory - but that makes you use Hibernate directly, not just under the hood
- Use JTA UserTransaction class
- Use EJBs transaction propagation. (requires an application server)
- Use Spring Transaction Management
And, if you refresh/clean the project now, you should see that the previous error (Table "DOGS" cannot be resolved) is now gone. You may have to do more Eclipse magic, like closing the database connection and opening it again etc.
Creating an entity from an existing database table
You can create entities from existing database tables, but it doesn't work so well, as I will show.
Imagine you already have an existing table in the database. Or, better, let's create one.
CREATE TABLE toys ( TOY_ID BIGINT NOT NULL PRIMARY KEY, TOY_NAME VARCHAR(128) NOT NULL, DOG_ID BIGINT NOT NULL )That's simple SQL code (compatible with HSQLDB) creates a table called toys with a single column primary key and a toy name. There's also a column DOG_ID which we are going to make a foreign key.
ALTER TABLE toys ADD FOREIGN KEY (DOG_ID) REFERENCES DOGS (ID) ON DELETE CASCADE
That's the foreign key. ON DELETE CASCADE means that if a dog is deleted - all its toys are deleted too. If a toy is deleted - the dog stays. Dogs don't share toys in our application.
Simply refreshing your database connection in Eclipse may not work (didn't work for me), so you may have to close it and open it again.
Now, right-click the entities package and select "New -> Entities From Tables":

Here, select our new table:

In the next window of the wizard it is possible to specify the associations. Please look at the following screenshot:

It is smart enough to suggest the right columns:

Now it's time to specify the kind of association. In our case, each dog has many toys.

If you now click Finish, there's a surprise waiting for you. The list of associations remains empty.

I don't understand why. The class it generates doesn't even compile, and has no track of the relationship. Even when I created the relationship in JPA myself:
@ManyToOne
public Dog getDog() {
return this.dog;
}
... and let it create the table in the database, and then from this Hibernate-made table I repeated the procedure - I got the same disappointing result. What am I missing here?
Hand-written associations
I gave up generating entities using Eclipse JPA, I wrote them myself. Look at the previous code sample. This gives a one way association from a toy to a dog. A dog entity is unaware of its toys. You would need a separate DAO (ToysDAO) to even save toys.
We can change this by making the association bidirectional. In the Dog class:
// ...
private List<Toy> toys = new ArrayList<Toy>();
// ...
@OneToMany(mappedBy = "dog", cascade = CascadeType.ALL)
public List<Toy> getToys() {
return toys;
}
public void setToys(List<Toy> toys) {
this.toys = toys;
}
CascadeType.ALL (javax.persistence.CascadeType) does the trick.
If you do not provide the mappedBy attribute, JPA will expect you to have another class just to "join" two tables together and you will get a validation error from Eclipse JPA:
Join table "DOGS_TOYS" cannot be resolved.
Cool.
Mapping a collection of Strings
Sometimes you want to map a collection of plain Objects and not entities. In our case, a dog can have some nicknames.
import javax.persistence.ElementCollection;
// ...
@ElementCollection(fetch = FetchType.EAGER)
public List<String> getNicknames() {
return nicknames;
}
// ...
An additional table is created:

You need JPA 2.0 for this; prior to JPA 2.0 you'd have to use Hibernate explicitly:
org.hibernate.annotations.CollectionOfElements
But not anymore, now you can use @ElementCollection like shown above.
Download source code for this article

5 comments: