Enterprise Java Development@TOPIC@

Chapter 26. JPA Entity Class Basics

26.1. Create POJO Class using descriptor
26.2. Create POJO Class using annotations
26.3. Summary

This chapter will take you through the steps to register a Java POJO with the JPA persistence unit using both orm.xml mapping-file descriptors and Java class annotations. It will also take you through the steps to define a POJO class legal to be used as JPA entity class.

JPA Classes are required to ...

  1. Create a POJO Java class in the ...mapped Java package

    package myorg.entityex.mapped;
    
    
    import java.util.Date;
    public class Animal {
        private int id;
        private String name;
        private Date dob;
        private double weight;
        
        public Animal(String name, Date dob, double weight) {
            this.name = name;
            this.dob = dob;
            this.weight = weight;
        }
        
        public int getId() { return id; }
        public void setId(int id) {
            this.id = id;
        }
        
        public String getName() { return name; }
        public void setName(String name) {
            this.name = name;
        }
        
        public Date getDob() { return dob; }
        public void setDob(Date dob) {
            this.dob = dob;
        }
        
        public double getWeight() { return weight; }
        public void setWeight(double weight) {
            this.weight = weight;
        }
    }
  2. Copy the existing AutoTest.java to AnimalTest.java and remove (or ignore) references to the Auto class from AnimalTest.java

  3. Attempt to persist the Animal by adding the following @Test method to the AnimalTest.java JUnit class.

    # src/test/java/myorg/entityex/AnimalTest.java
    
     
        @Test
        public void testCreateAnimal() {
            log.info("testCreateAnimal");
            Animal animal = new Animal("bessie", 
                    new GregorianCalendar(1960, 1, 1).getTime(), 1400.2);
            em.persist(animal);        
            
            assertNotNull("animal not found", em.find(Animal.class,animal.getId()));
        }
  4. Attempt to build and run your test. Your test should fail with the following error message. This means that although your class is a valid Java POJO, it has not been made known to the persistence unit as a JPA entity.

    testCreateAnimal(myorg.entityex.AutoTest): Unknown entity: myorg.entityex.mapped.Animal
    ...
    java.lang.IllegalArgumentException: Unknown entity: myorg.entityex.mapped.Animal
            at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:856)
            at myorg.entityex.AutoTest.testCreateAnimal(AutoTest.java:100)
    
  5. Add the POJO class to the persistence unit by adding an orm.xml JPA mapping file to your project. Place the file in the src/main/resources/orm directory.

    
    # src/main/resources/orm/Animal-orm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" version="2.0">

        <entity class="myorg.entityex.mapped.Animal"/>

    </entity-mappings>
  6. Register the orm.xml file with the persistence unit by adding a mapping-file element reference.

    
    # src/test/resources/META-INF/persistence.xml

        <persistence-unit name="entityEx-test">
            <provider>org.hibernate.ejb.HibernatePersistence</provider>

            <mapping-file>orm/Animal-orm.xml</mapping-file>
            <class>myorg.entityex.Auto</class>
            <properties>
            ...
  7. Attempt to build and run your test. Your test should fail with the following error message. The specifics of the error message will depend upon whether you are running just the JUnit test or building within Maven since the pom is configured to build database schema from the JPA mappings prior to running the JUnit test.

    
    PersistenceUnit: entityEx-test] Unable to configure EntityManagerFactory: No identifier specified for entity: myorg.entityex.mapped.Animal

    Caused by: org.hibernate.AnnotationException: No identifier specified for entity: myorg.entityex.mapped.Animal

    Although the class is a valid POJO and we followed the deployment descriptor mechanism for registering it with the persistence unit, it is not a legal entity. The error message indicates it is lacking a primary key field.

  8. Update the orm.xml file and define the "id" column as the primary key property for the entity.

    
        <entity class="myorg.entityex.mapped.Animal">
            <attributes>
                <id name="id"/>
            </attributes>
        </entity>
  9. Rebuild your module and it should now persist the POJO as a JPA entity. The SQL should be printed in the debug output.

    $ mvn clean test
    ...
    Hibernate: 
        insert 
        into
            Animal
            (dob, name, weight, id) 
        values
            (?, ?, ?, ?)
     -tearDown() complete, em=org.hibernate.ejb.EntityManagerImpl@12a80ea3
     -closing entity manager factory
     -HHH000030: Cleaning up connection pool [jdbc:h2:/home/jcstaff/workspaces/ejava-javaee/git/jpa/jpa-entity/entityEx/target/h2db/ejava]
    Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.94 sec
    
    Results :
    
    Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
    
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
  10. Update your JUnit test method to look like the following. The unit test now clears the cache of entities and forces the entity manager to instantiate a new instance for the value returned from the find().

        @Test
    
        public void testCreateAnimal() {
            log.info("testCreateAnimal");
            Animal animal = new Animal("bessie", 
                    new GregorianCalendar(1960, 1, 1).getTime(), 1400.2);
            em.persist(animal);        
            
            assertNotNull("animal not found", em.find(Animal.class,animal.getId()));
            
            em.flush(); //make sure all writes were issued to DB
            em.clear(); //purge the local entity manager entity cache to cause new instance
            assertNotNull("animal not found", em.find(Animal.class,animal.getId()));
        }
  11. Attempt to rebuild your module. It should fail because the entity class does not have a default constructor. Remember that default constructors are provided for free in POJOs until you add the first constructor. Once you add a custom constructor you are required to add a default constructor to make it a legal entity class.

    javax.persistence.PersistenceException: org.hibernate.InstantiationException: No default constructor for entity: myorg.entityex.mapped.Animal
    
  12. Update the POJO with a default constructor.

        public Animal() {} //must have default ctor
    
        public Animal(String name, Date dob, double weight) {
            this.name = name;
            this.dob = dob;
            this.weight = weight;
        }
  13. Rebuild the module. It should now pass because you have defined and registered a compliant entity class. The class was

  1. Copy the POJO class to a new java package and class name (Animal2).

    package myorg.entityex.annotated;
    
    
    import java.util.Date;
    public class Animal2 {
        private int id;
        private String name;
        private Date dob;
        private double weight;
        
        public Animal2() {} //must have default ctor
    ...
    }
  2. Add a javax.persistence.Entity annotation to the class

    import javax.persistence.Entity;
    
    
    @javax.persistence.Entity
    public class Animal2 {
  3. Register the new entity with the persistence.xml using a class element reference

    
        <persistence-unit name="entityEx-test">
            <provider>org.hibernate.ejb.HibernatePersistence</provider>

            <mapping-file>orm/Animal-orm.xml</mapping-file>
            <class>myorg.entityex.Auto</class>
            <class>myorg.entityex.annotated.Animal2</class>
            <properties>
  4. Add a new test method to work with the new class added to the module.

        @Test
    
        public void testCreateAnimalAnnotated() {
            log.info("testCreateAnimalAnnotated");
            myorg.entityex.annotated.Animal2 animal = new myorg.entityex.annotated.Animal2("bessie", 
                    new GregorianCalendar(1960, 1, 1).getTime(), 1400.2);
            em.persist(animal);        
            
            assertNotNull("animal not found", em.find(myorg.entityex.annotated.Animal2.class,animal.getId()));
            
            em.flush(); //make sure all writes were issued to DB
            em.clear(); //purge the local entity manager entity cache to cause new instance
            assertNotNull("animal not found", em.find(myorg.entityex.annotated.Animal2.class,animal.getId()));
  5. Attempt to build/run your module at this point. You should get a familiar error about Animal2 not having an identifier.

    Unable to configure EntityManagerFactory: No identifier specified for entity: myorg.entityex.annotated.Animal2
    
  6. Since we want to use annotations for the new class, fix the issue by adding a @javax.persistence.Id annotation to the id attribute. This is called FIELD access in JPA. You can alternately use PROPERTY access by moving the annotation to the getId() method.

        @javax.persistence.Id
    
        private int id;
  7. Re-run you test. It should succeed this time.

    $ mvn clean test
    ...
    [INFO] BUILD SUCCESS
    ...
  8. If you would like to observe the data in the database, do two things

  9. Type the following command in the H2 browser UI

    SELECT * FROM ANIMAL2;
    ID      DOB     NAME    WEIGHT  
    0   1960-02-01 00:00:00.0   bessie  1400.2

In this chapter you created two POJOs and correctly registered them with the persistence unit. One used a orm.xml descriptor and the other used Java annotations. Each entity had to be equipped with a default constructor and at least one property to use as the primary key.