CHAPTER 8

image

Object-Relational Mapping

For years, the Java Database Connectivity API (JDBC) was the standard for working with databases in a web or desktop Java application. Over the years, techniques for obtaining access to data stores and working with data within applications evolved, and many organizations began to develop their own strategies for working with data in a more convenient way. Developers often find it easier to work with Java objects rather than Structured Query Language (SQL) for relational data. Chapter 7 discusses some techniques that have been used in order to encapsulate SQL into separate utility classes and abstract it from developers so that they can work with Java objects rather than the SQL. Such strategies are known as object-relational mapping (ORM) strategies, and there are several well-known ORM strategies available from a multitude of organizations today.

Among the most well-known ORM strategies are Hibernate (http://hibernate.org), Oracle’s TopLink (www.oracle.com/technetwork/middleware/toplink/overview/index.html), and EclipseLink (http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development). In an effort to standardize the industry, the Java Persistence API (JPA)has been deemed the strategy to use for moving forward with Java Enterprise 7. The JPA includes many features that were first introduced in ORM strategies such as Hibernate and TopLink. In fact, some of the top representatives from many of the different ORM projects have come together to formulate the Java Specification Requests (JSRs) for Java EE 7 and beyond, providing Java enterprise developers with a standard, efficient, and highly productive way to work with an underlying RDBMS from within Java applications. The JPA allows developers to choose from a variety of Java persistence providers to utilize the configuration with which they are most comfortable, without the need to include multiple third-party libraries or customizations within the application. The possible providers are as follows:

  • EclipseLink (JPA default)
  • Hibernate
  • TopLink Essentials
  • KODO
  • OpenJPA

Object-relational mapping is the process of mapping a Java object to a database table, such that each column of the database table maps to a single field or property within the Java object. Java objects that are used to map against database tables are referred to as entity objects, and this chapter will focus on the creation and use of entity objects. Recipes will cover areas such as creating classes and performing standard database transactions. You will learn how to configure a connection against a database, how to persist and retrieve objects without using SQL, and how to relate objects to one another in a meaningful and productive manner.

Not only does ORM programming abstract the implementation details of working directly with a database from a developer, but it also provides a standard mechanism for deploying applications on databases from multiple vendors. The JPA takes care of translating code into SQL statements, so once an application is written using JPA, it can be deployed using almost any underlying database. The Java EE 7 platform introduces JPA 2.1, which includes more benefits such as support for multitenancy, support for stored procedures and vendor functions (Chapter 9), and more.

image Note   The recipes within this chapter may change depending upon which JPA provider you choose. For instance, providers may include a different set of metadata annotations to use. Rather than list each annotation that is available for use in each recipe, I will direct you to very good resources for learning about all of the possible annotations that can be used along with each of the most widely used providers. While most of the annotations are common among all providers, there are a handful of custom annotations for each.

EclipseLink: www.eclipse.org/eclipselink/api/2.2/org/eclipse/persistence/annotations/package-summary.html

Hibernate: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/

Toplink JPA (Java Persistence API): www.oracle.com/technetwork/middleware/ias/toplink-jpa-annotations-096251.html

image Note   The sources for Chapter 8 reside within the org.javaeerecipes.chapter08 package. To run the examples from Chapter 8, deploy the application to the application server, and then visit the URL http://localhost:8080/JavaEERecipes/faces/chapter08/home.xhtml. It should be noted that the examples for Chapter 8 cannot be run within a web application without the use of other technologies such as Enterprise JavaBeans, which will be covered in Chapter 9. For that reason, many of the examples in this chapter utilize stand-alone Java classes for testing purposes.

8-1. Creating an Entity

Problem

You want to create a Java object that can be mapped to a database table so that the class can be used for persistence along with the Enterprise JavaBeans (EJB) technology, rather than using JDBC.

Solution

Create an entity class against a particular database table. Declare persistent fields or properties for each of the columns in the underlying data store table and use annotations to map the fields to a given column. Provide getters and setters for each of the persistent fields or properties that are declared within the entity so that other classes can access the contents.

The following code is an entity class named BookAuthor08, which maps the BOOK_AUTHOR database table to a standard Java object for use within the application:

package org.javaeerecipes.chapter08.entity;

import java.io.Serializable;
import java.math.BigDecimal;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * Chapter 8
 * Entity class for the BOOK_AUTHOR database table of the Acme Bookstore application
 * @author juneau
 */
@Entity
public class BookAuthor implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 30)
    @Column(name = "LAST")
    private String last;
    @Size(max = 30)
    @Column(name = "FIRST")
    private String first;
    @Lob
    @Column(name = "BIO")
    private String bio;

    public BookAuthor() {
    }

    public BookAuthor(BigDecimal id) {
        this.id = id;
    }

    public BigDecimal getId() {
        return id;
    }

    public void setId(BigDecimal id) {
        this.id = id;
    }

    public String getLast() {
        return last;
    }

    public void setLast(String last) {
        this.last = last;
    }

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public String getBio() {
        return bio;
    }

    public void setBio(String bio) {
        this.bio = bio;
    }

    @Override

    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof BookAuthor)) {
            return false;
        }
        BookAuthor other = (BookAuthor) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "org.javaeerecipes.chapter08.entity.BookAuthor[ id=" + id + " ]";
    }
    
}

The entity itself cannot be used alone to access the database. Minimally, a persistence unit is required in order to connect with a database and work with the entity classes. To learn more about creating a persistence unit, please refer to Recipe 8-3.

How It Works

As an object-oriented developer, it sometimes makes more sense to work with objects that represent data, rather than working with variables of data and writing SQL to work directly with the underlying data store. The concept of mapping objects to database tables is better known as object-relational mapping. The Java Persistence API utilizes ORM for storing and retrieving data from a database via the usage of object classes known as entity classes. An entity class is a Java object that represents an underlying database table.

image Note   Prior to EJB 3.0, XML files were used instead of annotations in order to manage metadata for entity classes. You can still use XML descriptors to manage metadata today, but I will not cover how to do so in this text. Most ­annotations can be used to selectively override default values within a class.

The entity class is usually named the same as the underlying database table, using camel-case lettering (capitalized first letters for all words except for the first) to separate different words within the table name. For instance, the BOOK_AUTHOR database table has a Java entity class named BookAuthor. The name of the entity can differ from the name of the underlying database table. However, it is a standard practice to name the entity class the same. In such cases where the name of the entity class has to differ from the database table, the @Table annotation can be used to annotate the entity class, providing the name of the underlying data table. Every entity class must be annotated as such by specifying the javax.persistence.Entity annotation. In the example, the BookAuthor entity class specifies only those annotations that are required. If the entity were to be named differently than the database table, the @Table annotation could be utilized as follows:

...
@Entity
@Table(name = "BOOK_AUTHOR")
...

An entity class must have a public or protected no-argument constructor. It is always a good idea to make an entity class Serializable by implementing the java.io.Serializable interface because doing so ensures that the entity class may be passed by value. All entity classes must contain private or protected instance variables for each of the columns within the underlying database table, as well as variables for each relationship that the entity may have with other entities. (To read more about entity relationships, please take a look at Recipes 8-6, 8-7, and 8-8.) All database tables that will be mapped to Java entity classes must contain a primary key field, and the corresponding instance variable within the entity class that maps to the primary key column must be annotated with @Id. Each of the instance variables that maps to a database column can be annotated with @Column, specifying the name of the underlying database column. However, if no @Column annotation is specified, the name of the variable should match the database column name exactly, using camel-case lettering for any separate words within the column name. To signify that a particular database column and its mapped instance variable cannot contain a NULL value, the variable can be annotated with @Basic(optional=false), as shown in the example. You may also specify the @NotNull annotation on any variable that should not contain a NULL value.

Another annotation of note that is used within the example for this recipe includes @Size, which is used to specify the maximum size for a String variable. The size value should correspond to the database column size for the corresponding column. In addition, the @Lob annotation can be used to signify that the underlying database data type is a large object. There are other annotations that can be used to further customize an entity class; please see the link within the introduction of this book for the JPA provider that you are using in order to learn more about all of the annotations that can be used. Table 8-1 summarizes the most commonly used annotations when creating an entity class. Those annotations are covered within the solution to this recipe.

Table 8-1. Commonly Used Annotations for Creating Entity Classes

Annotation Description
@Entity Designates a plain old Java object (POJO) class as an entity so that it can be used with JPA services
@Table (optional) Specifies the name of the primary table associated with an entity
@Id Designates one or more persistent fields or properties of the entity’s primary key
@Basic Configures the fetch type to LAZY
@Column Associates a persistent attribute with a different name if the column name is awkward, incompatible with a preexisting data model, or invalid as a column name in your database

As mentioned in the solution for this recipe, an entity class cannot be used by itself. It is part of an overall solution for working with an underlying data source. Entity classes make it easy to map Java objects to database tables. They should be used in tandem with Enterprise JavaBeans (EJB) classes (Chapter 9) or stand-alone with a persistence unit (Recipe 8-3) to perform database operations. A full Java EE solution utilizing the JSF framework also uses JSF managed beans to work directly with EJBs, which, in turn, conduct work via the entity classes.

image Note   You may be wondering why the hashCode() and equals() methods are overridden in the example. The equals() method is present in every Java object, and it is used to determine object identity. Every entity class needs to contain an implementation of these methods in order to differentiate objects from one another. It is very possible for two entity objects to point to the same row in a database table. The equals() method can determine whether two entities both point to the same row. Moreover, all Java objects that are equal to one another should contain the same hashCode. In entity classes, it is important to override these methods to determine whether objects represent the same database table row.

8-2. Mapping Data Types

Problem

You are interested in mapping database table columns with entity class fields, but you are unsure which data types to declare for the fields within the class.

image Note   Transient fields or properties cannot contain mapping annotations. A transient field or property is not persisted to the database.

Solution

Map database table column data types with their equivalent data type in the Java language specification when declaring instance variables for the columns within an entity class. The Java EE container will convert the database value accordingly so long as the database column data type matches up to a Java data type that will contain the specified column’s value. To demonstrate data type mapping, an entity class will be written for the Acme Bookstore’s CONTACT database table. The CONTACT table has the following description:

SQL> desc contact
 Name                             Type
 -------------------------------  ----------------------------
 ID                               NOT NULL NUMBER
 FIRST                            VARCHAR2(50)
 LAST                             VARCHAR2(50)
 EMAIL                            VARCHAR2(150)
 PASSWORD                         VARCHAR2(30)
 DESCRIPTION                      CLOB
 OCCUPATION                       VARCHAR2(150)
 RECEIVENOTIFICATIONS             VARCHAR2(1)
 GENDER                           VARCHAR2(1)

The corresponding entity class is named Contact, and its class listing, shown next, demonstrates how to match each database column type to an appropriate Java data type:

package org.javaeerecipes.chapter08.entity;
...

@Entity
@Table(name = "CONTACT")
public class Contact implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    @Basic(optional = false)
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 50)
    @Column(name = "FIRST")
    private String first;
    @Size(max = 50)
    @Column(name = "LAST")
    private String last;
    @Size(max = 150)
    @Column(name = "EMAIL")
    private String email;
    @Size(max = 30)
    @Column(name = "PASSWORD")
    private String password;
    @Lob
    @Column(name = "DESCRIPTION")
    private String description;
    @Size(max = 150)
    @Column(name = "OCCUPATION")
    private String occupation;
    @Size(max = 1)
    @Column(name = "RECEIVENOTIFICATIONS")
    private String receivenotifications;
    @Size(max = 1)
    @Column(name = "GENDER")
    private String gender;

    public Contact() {
    }

    ...

// getters and setters

    ...

    @Override
    public int hashCode() {
        ...
    }

    @Override
    public boolean equals(Object object) {
        ...
    }

    @Override
    public String toString() {
        return "org.javaeerecipes.chapter08.entity.Contact[ id=" + id + " ]";
    }
    
}

It is important to specify the correct mapping data types because errors can occur down the line if not done correctly. Such is often the case with numerical data types.

How It Works

To create a Java class that will be used to represent a database table, you must map each of the table’s columns to a class instance variable. In doing so, the variable must be assigned a data type that corresponds to that database column’s data type. In some cases, more than one Java data type will map to a single database column’s data type. In other cases, however, a database column’s data type must match up to a specific Java data type. Table 8-2 lists the different Java data types and their associated Oracle database data type. If you are using another database for your work, please see the documentation for the database to rectify any discrepancies between the data types from those used by Oracle.

Table 8-2. Oracle Database and Java Data Type Mapping

Oracle Data Type Java Data Types
BINARY_INTEGER, NATURAL, NATURALN, PLS_INTEGER, POSITIVE, POSITIVEN, SIGNTYPE, INT, INTEGER int
CHAR, CHARACTER, VARCHAR2LONG, STRING, VARCHAR java.lang.String
RAW, LONG RAW byte[]
DEC, DECIMAL, NUMBER java.math.BigDecimal
DOUBLE PRECISION, FLOAT double
SMALLINT int
REAL float
DATE java.sql.Timestamp
java.sql.Date
TIMESTAMP (or derivative) java.sql.Timestamp
BOOLEAN boolean
CLOB java.sql.Clob
BLOB java.sql.Blob
VARRAY java.sql.Array
REF CURSOR java.sql.ResultSet

Mapping data types correctly is a very important step in the creation of an entity class because an incorrect mapping can result in incorrect precision for numerical values and so forth. Utilizing the correct data types when mapping entity classes to the database table may vary depending upon database vendor, but Table 8-2 should be easily translated from Oracle data types to the data types for the RDBMS of your choice.

8-3. Creating a Persistence Unit

Problem

You want to use an entity class to perform database transactions.

Solution

Create a persistence unit based upon a database connection, and then use the persistence unit to perform transactions with a given entity class. A persistence unit can use a database connection pool configured within an application server, or it can utilize a local JDBC configuration in order to obtain a database connection. In this example, I will demonstrate the use of the local JDBC configuration since the example will be run as a stand-alone application, rather than being deployed to an application server.

The following persistence unit is configured to create local JDBC connections, rather than using JPA for connections. However, you can learn more about configuring a persistence unit to work with database connection pools that are configured within an application server in the “How it Works” section of this recipe. The following code is from a file named persistence.xml, which is located in the srcconf directory for Chapter 8:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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/persistencehttp://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="JavaEERecipesLOCAL" transaction-type="RESOURCE_LOCAL">
        <class>org.javaeerecipes.chapter08.entity.BookAuthor</class>
        <properties>
            <property name="javax.persistence.jdbc.user" value="username"/>
            <property name="javax.persistence.jdbc.password" value="password"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@hostname:1521:dbname"/>
        </properties>
    </persistence-unit>
</persistence>

How It Works

To work with a database, an application needs to have the ability to connect. Usually a database connection pertains to a single user name/password within a database. The persistence context XML file is where the connection information for the Java Persistence API resides. A persistence context can contain configuration for more than one connection to the database. Each connection configuration is referred to as a persistence unit, and each has a unique name that is used to identify the connection from within the application classes. The persistence.xml file can be packaged as part of a web archive (WAR) or enterprise archive (EAR) file, or it can be packed into a JAR file, which is, in turn, packaged with a WAR or EAR. If packaged with an EAR file, it should reside within the META-INF directory. If using a WAR file, the persistence.xml file should be packaged within the WEB-INF/classes/META-INF directory. Lastly, if packaging into a JAR file, the JAR should reside within the WEB-INF/lib directory of a WAR or the library directory of an EAR.

As mentioned previously, each persistence.xml file can contain more than one database configuration, or persistence unit. Each persistence unit contains the type of JPA provider that will be used for the connection, the transaction type (JTA or RESOURCE_LOCAL), classes to be used for persistence (entity classes), and database connection specifics. In this section, I will break down the persistence unit that is configured for the recipe solution and describe each piece.

At the root of each persistence unit is the persistence-unit element, which contains the name and transaction-type attributes. Each persistence unit has a name; in the case of the example, it is JavaEERecipesLOCAL, and this name is used to obtain a reference to the persistence unit from within application code. The transaction-type attribute of a persistence unit indicates whether Java Transaction API entity managers will be created (for use within an application server) or Resource-Local entity managers will be created (for use with stand-alone applications).

Next in the example you will see a series of classes listed within separate class elements. Within the persistence-unit element, zero or more classes can be identified for use with the persistence unit. These classes are the entity classes that will be mapped to the underlying database table. If using the RESOURCE_LOCAL transaction type, each entity class must be listed within the persistence unit. If using JTA (deployed to an application server within a WAR or EAR file), then the container takes care of identifying the entity classes and they do not need to be listed in the persistence unit. If an entity class is not identified in the persistence unit and the transaction type is RESOURCE_LOCAL, then that entity class will not be available for use within the application.

image Note   A persistence unit may also include an <exclude-unlisted-classes> element, which should be set to a Boolean value. This element is used to indicate whether classes must be listed using a <class> element within the persistence unit when using JTA, and it is FALSE by default. It may make sense to set this element to TRUE if two or more data sources are being used within an application and only specified entity classes should be used for each.

The properties element should contain subelements that identify the connection to the database. Specifically, the user, password, and database URL are identified within subproperties of the properties element. For RESOURCE_LOCAL persistence units, the following points are true:

  • The property javax.persistence.jdbc.username should be used to identify the database user name for the connection.
  • The property javax.persistence.jdbc.password should identify the database user password for the connection.
  • The property javax.persistence.jdbc.url should identify the database URL for the connection.

The properties for a Java Transaction API connection are different. In fact, for JTA, there can be no properties specified. Instead, an element named jta-data-source can be used to specify a JNDI name of a database connection that has been configured within the application server for use. For example, let’s say the database connection is configured as jdbc/OracleConnection within the application server. Furthermore, let’s assume you are deploying a WAR file to the GlassFish application server, and you will use JTA instead of RESOURCE_LOCAL. If this is the case, the persistence unit may look like the following:

<persistence-unit name="JavaEERecipesJTA" transaction-type="JTA">
    <jta-data-source>jdbc/OracleConnection</jta-data-source>
    <properties/>
</persistence-unit>

image Note   There are no classes listed in the JTA example because the application server automatically identifies the ­entity classes for use with the persistence unit. However, there are circumstances for which it may be useful to list classes, as mentioned in the preceding note.

To use a persistence unit, an EntityManagerFactory object must first be obtained. An EntityManagerFactory object can be obtained by calling the Persistence.createEntityManagerFactory method and passing the string-based name of the persistence unit for which you want to obtain a connection. Once an EntityManagerFactory object has been obtained, an EntityManager object can be created and used to begin a database transaction. Obtaining a connection via a persistence unit would look similar to the following:

...
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JavaEERecipesLOCAL");
EntityManager em = emf.createEntityManager();
try {
    EntityTransaction entr = em.getTransaction();
    entr.begin();
    Query query = em.createNamedQuery("BookAuthor.findAll");
...

image Note   The preceding example uses createNamedQuery in order to substitute a named query rather than writing the JPQL inline. For more information, please see Recipe 8-9.

The persistence.xml configuration file contains the database connection information that will be utilized by an application to work with database(s). If you are working with JPA, you will become very familiar with creating a persistence unit, whether using local JDBC connections or an application server connection pool.

8-4. Using Database Sequences to Create Primary Key Values

Problem

Your database contains sequences that are used to generate primary key values for your database table records. Your application needs to use the database sequences in order to assign primary key values when creating and persisting objects.

Solution

Annotate an entity class’s primary key field with a SequenceGenerator and then associate it with an entity Generator in order to utilize a database sequence for populating a database table column value. In the following example, the BookAuthor entity has been updated to utilize the BOOK_AUTHOR_S database sequence for creating primary key values. As such, the id field has been annotated accordingly.

package org.javaeerecipes.chapter08.entity;

import java.io.Serializable;
import java.math.BigDecimal;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * Chapter 8
 * Entity class for the BOOK_AUTHOR database table of the Acme Bookstore application
 * @author juneau
 */
@Entity
@Table(name = "BOOK_AUTHOR")
public class BookAuthor implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="book_author_s_generator",sequenceName="book_author_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="book_author_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;

    @Size(max = 30)
    @Column(name = "LAST")
    private String last;
    @Size(max = 30)
    @Column(name = "FIRST")
    private String first;
    @Lob
    @Column(name = "BIO")
    private String bio;

    public BookAuthor() {
    }

}    ...

When a new BookAuthor object is persisted to the database, the next sequence value for BOOK_AUTHOR_S will be used as the primary key value for the new database record. The class org.javaeerecipes.chapter08.recipe08_04.SequenceTest.java can be run to test the sequence-generated primary key once the persistence context has been configured for the local JDBC database connection (see Recipe 8-3 for details). The following excerpt is taken from the SequenceTest class, and it demonstrates how to add a new BookAuthor object to the database:

...
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JavaEERecipesLOCAL");
EntityManager em = emf.createEntityManager();
try {
    EntityTransaction entr = em.getTransaction();
    entr.begin();
    BookAuthor author = new BookAuthor();
    author.setFirst("JOE");
    author.setLast("TESTER");
    author.setBio("An author test account.");
    boolean successful = false;
    try {
        em.persist(author);
        successful = true;
    } finally {
        if (successful){
            entr.commit();
        } else {
            entr.rollback();
        }
    }
    Query query = em.createNamedQuery("BookAuthor.findAll");
    List authorList = query.getResultList();
    Iterator authorIterator = authorList.iterator();
    while (authorIterator.hasNext()) {
        author = (BookAuthor) authorIterator.next();
        System.out.print("Name:" + author.getFirst() + " " + author.getLast());
        System.out.println();
    }
} catch (Exception ex){
    System.err.println(ex);
} finally {
    em.close();
}
...

image Note   This example demonstrates the use of transactions. Transactions allow for an entire sequence of processes to be performed at once. If a failure occurs in one of the processes, then all processes in the transaction fail, and changes to the database are rolled back. Otherwise, if all processes in the transaction complete successfully, then they are committed to the database. Transactions are very useful in situations where multiple database events depend upon one another.

How It Works

In many cases, it makes sense to generate primary key values for database table records via a database sequence. Utilizing JPA allows you to do so by incorporating the use of the @SequenceGenerator and @GeneratedValue annotations into an entity class. Every database table that is mapped to an entity class must have a primary key value, and using database sequences to obtain those values makes sense for many reasons. For instance, in some cases an application administrator will need to know what the next number, current number, or last number used for a primary key value might be. By using a database sequence, gathering information regarding the next, current, or last numbers is just a query away.

The @SequenceGenerator annotation should be placed directly before the declaration of the primary key field or property within the entity class, or it can be placed before the entity class declaration. Note that other annotations may be placed between the @SequenceGenerator annotation and the actual variable declaration. The @SequenceGenerator annotation accepts values regarding the database sequence that is to be used for primary key generation. More specifically, the annotation accepts the following attributes:

  • name (required): The name of the generator (this name can be an arbitrary value)
  • sequenceName (optional): The name of the database sequence from which to obtain the primary key value
  • initialValue (optional): The initial value of the sequence object
  • allocationSize (optional): The amount of increment when allocating numbers from the sequence

The @GeneratedValue annotation provides for the specification of the primary key generation strategy for the entity. Similarly to the @SequenceGenerator attribute, it can be placed before the declaration of the primary key field or property within the entity class, or it can be placed before the entity class declaration. It is used to specify the means for which the entity class primary key will be generated. The three options are as follows:

  • The entity class will generate its own primary key value before inserting a new record.
  • The entity class will use a database sequence for the key generation.
  • The entity class will generate keys via some other means.

The attributes that can be specified for the @GeneratedValue annotation are as follows:

  • generator (optional): This is the name of the primary key generator to use as specified by the @SequenceGenerator annotation. This must match the name attribute that was supplied for the @SequenceGenerator annotation unless using an @TableGenerator. This defaults to the ID generator supplied by the persistence provider.
  • strategy (optional): This is the primary key generation strategy that will be used by the persistence provider to generate the primary key for the annotated field or entity class. This defaults to AUTO if not supplied.

The strategy attribute of @GeneratedValue can accept four different javax.persistence.GenerationType Enum values.

  • AUTO: Indicates that the persistence provider should choose an appropriate strategy for a particular database
  • IDENTITY: Indicates that the persistence provider must assign primary keys for the entity using the database identity column
  • SEQUENCE: Indicates that the persistence provider must assign primary keys for the entity using the database sequence column
  • TABLE: Indicates that the persistence provider must assign primary keys for the entity using an underlying database table to ensure unique values are provided

In the example for this recipe, the BOOK_AUTHOR_S database sequence is specified for the sequenceName attribute of the @SequenceGenerator annotation, and the name of the generator is book_author_s_generator. Note that the @GeneratedValue name attribute matches that of the @SequenceGenerator annotation; this is very important! Once specified, the entity class will automatically obtain the next value from the database sequence when a new object is persisted.

image Note   There are other options for generating key values, such as AUTO, IDENTITY, and TABLE. Those strategies can be valid in different situations. For more information on using other options, please refer to the online Java EE 7 documentation at http://docs.oracle.com/javaee/7/tutorial/doc/.

8-5. Generating Primary Keys with More Than One Attribute

Problem

A particular database table does not contain a primary key, and you need to join the values of two or more of the table columns in order to create a primary key for each record.

Solution #1

Create a composite primary key by developing an embedded composite primary key class and denoting the composite key field within an entity using the javax.persistence.EmbeddedId and javax.persistence.IdClass annotations. Consider the AUTHOR_WORK database table that is used for the Acme Bookstore application. Suppose that the AUTHOR_WORK database table did not contain a primary key column. It would be possible to generate a primary key for each record based upon its BOOK_ID and AUTHOR_ID columns. The following entity class is that for the AuthorWork entity. Instead of using the ID column as a primary key, it uses both the bookId and authorId columns together to formulate a composite primary key.

package org.javaeerecipes.chapter08.entity;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import org.javaeerecipes.chapter08.entity.key.AuthorWorkPKEmbedded;
import org.javaeerecipes.chapter08.entity.key.AuthorWorkPKNonEmbedded;

/**
 * Chapter 8 - Example of Embedded Primary Key
 * @author juneau
 */

@Entity
@Table(name = "AUTHOR_WORK")
//  (Named queries are covered in Recipe 8-9)
@NamedQueries({
    @NamedQuery(name = "AuthorWork.findAll", query = "SELECT a FROM AuthorWork a")})
public class AuthorWorkEmbedded implements Serializable {
    private static final long serialVersionUID = 1L;
    
    // You can use an embedded ID in-place of a standard Id if a table
    // contains more than one column to compose a primary key.  Comment
    // out along with the getters and setters to use a non-embeddable primary key.
    @EmbeddedId
    private AuthorWorkPKEmbedded embeddedId;

    public AuthorWorkEmbedded() {
    }

    public AuthorWorkEmbedded(BigInteger bookId, BigInteger authorId) {
        this.embeddedId = new AuthorWorkPKEmbedded(bookId, authorId);
    }

 
    /**
     * @return the embeddedId
     */
    public AuthorWorkPKEmbedded getEmbeddedId() {
        return embeddedId;
    }

    /**
     * @param embeddedId the embeddedId to set
     */
    public void setEmbeddedId(AuthorWorkPKEmbedded embeddedId) {
        this.embeddedId = embeddedId;
    }
    
}

To utilize an embedded primary key, you must create a class that contains the logic for mapping the primary key ID to the columns that are used to compose it. For this example, the AuthorWorkPKEmbedded class serves this purpose, which is shown here:

package org.javaeerecipes.chapter08.entity.key;

import java.io.Serializable;
import java.math.BigInteger;
import javax.persistence.Embeddable;

/**
 * Embeddable Primary Key class for AuthorWork
 *
 * @author juneau
 */
@Embeddable
public class AuthorWorkPKEmbedded implements Serializable {

    private BigInteger bookId;
    private BigInteger authorId;

    public AuthorWorkPKEmbedded() {
    }
    
    public AuthorWorkPKEmbedded(BigInteger bookId, BigInteger authorId){
        this.bookId = bookId;
        this.authorId = authorId;
    }

    /**
     * @return the bookId
     */
    public BigInteger getBookId() {
        return bookId;
    }

    /**
     * @param bookId the bookId to set
     */
    public void setBookId(BigInteger bookId) {
        this.bookId = bookId;
    }

    /**
     * @return the authorId
     */
    public BigInteger getAuthorId() {
        return authorId;
    }

    /**
     * @param authorId the authorId to set
     */
    public void setAuthorId(BigInteger authorId) {
        this.authorId = authorId;
    }

    public int hashCode() {
        return bookId.hashCode() + authorId.hashCode();
    }

    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof AuthorWorkPKEmbedded)) {
            return false;
        }
        if (obj == null) {
            return false;
        }
        AuthorWorkPKEmbedded pk = (AuthorWorkPKEmbedded) obj;
        return (((bookId == ((AuthorWorkPKEmbedded) obj).getBookId()))
                && ((authorId == ((AuthorWorkPKEmbedded) obj).getAuthorId())));
    }
}

image Note   Although the preceding example is not an entity class, it is persisted. Even if the members are not designated as @Basic, they are still persisted.

Both the hashCode() and equals() methods must be present in composite key classes.

Solution #2

Create a composite primary key by developing a nonembedded composite primary key class, and denote two or more of the columns within the entity class with the @Id annotation. Also, if using a nonembedded primary key class, the entity class must be designated as such by utilizing the @IdClass annotation and specifying the nonembedded primary key class.

Consider the AUTHOR_WORK database table that is used for the Acme Bookstore application. Suppose that the AUTHOR_WORK database table did not contain a primary key column. It would be possible to generate a primary key for each record based upon its BOOK_ID and AUTHOR_ID columns. The following entity class is that for the AuthorWork entity. Instead of using the ID column as a primary key, it uses both the bookId and authorId columns together to formulate a composite primary key:

package org.javaeerecipes.chapter08.entity;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import org.javaeerecipes.chapter08.entity.key.AuthorWorkPKEmbedded;
import org.javaeerecipes.chapter08.entity.key.AuthorWorkPKNonEmbedded;

/**
 * Chapter 8 - Example of Non-Embedded Primary Key
 * @author juneau
 */

@IdClass(AuthorWorkPKNonEmbedded.class)
@Entity
@Table(name = "AUTHOR_WORK_LEGACY")
@NamedQueries({
    @NamedQuery(name = "AuthorWork.findAll", query = "SELECT a FROM AuthorWork a")})
public class AuthorWorkNonEmbedded implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    @Column(name = "BOOK_ID")
    private BigInteger bookId;
    
    @Id
    @Column(name= "AUTHOR_ID")
    private BigInteger authorId;

    public AuthorWorkNonEmbedded() {
    }

    public AuthorWorkNonEmbedded(BigInteger bookId, BigInteger authorId) {
        this.bookId = bookId;
        this.authorId = authorId;
    }

    public BigInteger getBookId() {
        return bookId;
    }

    public void setBookId(BigInteger bookId) {
        this.bookId = bookId;
    }

    public BigInteger getAuthorId() {
        return authorId;
    }

    public void setAuthorId(BigInteger authorId) {
        this.authorId = authorId;
    }
    
}

The associated nonembeddable primary key class is named AuthorWorkPKNonEmbedded. The code for this class is as follows:

package org.javaeerecipes.chapter08.entity.key;
import java.io.Serializable;
import java.math.BigInteger;

/**
 * Non-Embeddable Primary Key class for AuthorWork
 *
 * @author juneau
 */
public class AuthorWorkPKNonEmbedded implements Serializable {

    private BigInteger bookId;
    private BigInteger authorId;

    public AuthorWorkPKNonEmbedded() {
    }

    /**
     * @return the bookId
     */
    public BigInteger getBookId() {
        return bookId;
    }

    /**
     * @param bookId the bookId to set
     */
    public void setBookId(BigInteger bookId) {
        this.bookId = bookId;
    }

    /**
     * @return the authorId
     */
    public BigInteger getAuthorId() {
        return authorId;
    }

    /**
     * @param authorId the authorId to set
     */
    public void setAuthorId(BigInteger authorId) {
        this.authorId = authorId;
    }

    public int hashCode() {
        return bookId.hashCode() + authorId.hashCode();
    }

    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof AuthorWorkPKEmbedded)) {
            return false;
        }
        if (obj == null) {
            return false;
        }
        AuthorWorkPKEmbedded pk = (AuthorWorkPKEmbedded) obj;
        return (((bookId == ((AuthorWorkPKEmbedded) obj).getBookId()))
                && ((authorId == ((AuthorWorkPKEmbedded) obj).getAuthorId())));
    }
}

image Note   Although the AuthorWorkPKNonEmbedded class is not an entity, its members are persisted.

How It Works

There can be situations in which a database table may not contain a single primary key value to uniquely identify each row. Oftentimes this can be the case when working with legacy databases. In the Java Persistence API, all entity classes must contain a primary key that can be used to uniquely identify an object. To get around this obstacle when working with tables that do not contain a single primary key value, a composite primary key can be used to uniquely identify an object. A composite primary key is composed of two or more fields or properties within an entity class that can be combined together to create a unique identifier. Think in terms of performing a database query and attempting to return a record that matches only certain criteria. In such a case, you often need to include multiple relationships within the SQL WHERE clause. Creating a composite primary key within an entity class is basically the same concept in that you are telling JPA to use all of the fields or properties designated within the composite key in order to uniquely identify an object.

There are a couple of different techniques, embeddable and nonembeddable, that can be used to develop a composite primary key. The two techniques are similar in that they each require the creation of a separate class to compose the primary key, but they differ by the way in which the primary key is denoted within the entity class. In fact, the separate primary key class in both techniques can be created almost identically, except that an embeddable primary key class must be annotated using @Embeddable, as demonstrated in Solution #1 to this recipe. An entity with an embeddable primary key class should contain only a single primary key, and the data type for the primary key should be the same as the embeddable primary key class. That is, the primary key class should be declared within the entity using a private modifier, along with all of the other persistent properties and fields, and it should be annotated with @Id to indicate that it is the primary key. The following excerpt from Solution #1 shows how this is done:

@EmbeddedId
private AuthorWorkPKEmbedded embeddedId;

The entity class containing an embedded primary key should contain a constructor that accepts one parameter for each of the persistent fields or properties used for the primary key. Within the constructor, a new instance of the embeddable primary key class should then be instantiated using the passed-in arguments. The entity class using an embeddable primary key should contain accessor methods for the primary key field or property. However, unlike most entity classes, the hashCode() and equals() methods are not present because they are within the primary key class instead. Now that I’ve gone over the logistics of an entity class that uses an embeddable primary key, let’s take a look at the embeddable primary key class itself to see how it works.

A primary key class that is used for creating an embeddable primary key should contain declarations for each of the persistent fields or properties that will be used to compose the primary key for the associated entity class. Of course, these fields or properties should be made private, and there should be corresponding getters and setters for accessing the fields. The embeddable primary key class should be annotated with @Embeddable. It can contain two constructors: one that accepts no arguments and another optional constructor that accepts an argument for each of the persistent fields or properties that compose the primary key. Remember how the entity class that uses the embeddable primary key contains no hashCode() method? That is because it resides within the primary key class, and it simply adds together the hashCodes for each of the fields used to compose the primary key, and it returns the sum. The most important piece of the primary key class is the equals() method since it is used to determine whether an object or database record uniquely matches the associated primary key. The equals() method should accept an argument of type Object, which will be the object that is being compared against the current primary key object. The object is then compared to determine whether it is equal to the current primary key object, and if so, a true is returned. If not equal, then the object is compared to determine whether it is the same type of class as the embeddable primary key class, and a false is returned if it is not the same type. A false is also returned if the object is NULL. Finally, if a Boolean has not yet been returned based upon the conditionals that have been tested, then the object is casted into the same type of object as the primary key class, and each of its fields or properties is compared against those in the current primary key class. If equal, then a true is returned; if not equal, then a false is returned. The following lines of code demonstrate the equals() method:

public boolean equals(Object obj) {
    if (obj == this) {
        return true;
    }
    if (!(obj instanceof AuthorWorkPKEmbedded)) {
        return false;
    }
    if (obj == null) {
        return false;
    }
    AuthorWorkPKEmbedded pk = (AuthorWorkPKEmbedded) obj;
    return (((bookId == ((AuthorWorkPKEmbedded) obj).getBookId()))
            && ((authorId == ((AuthorWorkPKEmbedded) obj).getAuthorId())));
}

Solution #2 covers the use of a nonembedded primary key. The generation of a nonembeddable primary key is sometimes preferred over the use of an embedded primary key because some believe that the resulting entity class is easier to read. The overall construction of a nonembeddable primary key is basically the same, although there are a few subtle differences. For instance, when developing the primary key class for the nonembeddable primary key, there is no @Embeddable annotation on the class. The second difference that you may notice from the code in Solution #2 is that there is only one constructor used. Of course, an optional second constructor can still be created, accepting an argument for each of the persistent fields or properties that are used to compose the primary key.

Most differences take place within the entity class itself. To use a nonembedded composite primary key, the entity class must be annotated with @IdClass, naming the class that is used to construct the composite primary key. In the case of Solution #2, the @IdClass is as follows:

@IdClass(AuthorWorkPKNonEmbedded.class)

The second big difference in an entity class that uses a nonembeddable composite primary key is that instead of declaring one persistent field or property as an ID using the @Id annotation, the two or more fields or properties that are used to compose the primary key for the entity are declared directly within the entity, and each of them is annotated accordingly. The rest of the implementation is the same as an entity that uses an embedded composite primary key.

Which type of composite key you decide to use is completely a personal preference. Many people use a nonembeddable primary key to make the entity class easier to follow, in that it resembles a standard entity class more closely than an entity class using an embeddable composite primary key. In the end, both will produce the same result and allow entity classes to be created for those database tables that do not contain a single primary key field.

8-6. Defining a One-to-One Relationship

Problem

A database table that is used by your application contains data that has a one-to-one reference with data records from another table. As such, you want to create a one-to-one relationship between two entity objects within your application.

Solution

Create an association between the two tables that have a one-to-one relationship by declaring each of the entity classes themselves as persistent fields or properties within each other using an “owned” relationship, and annotate those fields with @OneToOne. For instance, let’s say that each record within the AUTHOR database table can be associated to a record in another table named AUTHOR_DETAIL, and vice versa. The AUTHOR_DETAIL table contains contact information for the author, so, in fact, these tables have a one-to-one relationship. To correlate them to each other from within the entity classes, specify the @OneToOne annotation on the field or property that is associated with the corresponding entity class. To have the ability to obtain the full author information from either table, a bidirectional one-to-one relationship needs to be created.

image Note   A one-to-one mapping could be unidirectional or bidirectional. A unidirectional mapping contains only an @OneToOne annotation on the owning entity for the corresponding entity class.

image Note   A relationship is referred to as owned if one entity contains a reference to another entity object referring to the entity itself. On the other hand, a relationship where an entity refers to another entity by primary key value is known as an unowned relationship.

In this solution, the Author entity would contain a @OneToOne reference for the AuthorDetail entity to create a bidirectional one-to-one mapping. In this code excerpt from the Author entity, the Author entity is the owning entity:

...
@Entity
@Table(name = "AUTHOR")

public class Author implements Serializable {
    
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="author_s_generator",sequenceName="author_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="author_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 30)
    @Column(name = "LAST")
    private String last;
    @Size(max = 30)
    @Column(name = "FIRST")
    private String first;
    @Lob
    @Column(name = "BIO")
    private String bio;
    @OneToOne
    private AuthorDetail authorId;

    public Author() {
    }

...

An excerpt for the entity class for the AUTHOR_DETAIL table is shown next. Of course, it has the name of AuthorDetail, and it contains a reference to the Author entity class.

...
@Entity
@Table(name = "AUTHOR_DETAIL")

public class AuthorDetail implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="author_detail_s_generator",sequenceName="author__detail_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="author_detail_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 200)
    @Column(name = "ADDRESS1")
    private String address1;
    @Size(max = 200)
    @Column(name = "ADDRESS2")
    private String address2;
    @Size(max = 250)
    @Column(name = "CITY")
    private String city;
    @Size(max = 2)
    @Column(name = "STATE")
    private String state;
    @Size(max = 10)
    @Column(name = "ZIP")
    private String zip;
    @Column(name = "START_DATE")
    @Temporal(TemporalType.DATE)
    private Date startDate;
    @Lob
    @Column(name = "NOTES")
    private String notes;
    @OneToOne(optional=false, mappedBy="authorDetail")
    private Author authorId;

    public AuthorDetail() {
    }
...

How It Works

It is not uncommon in the world of relational databases to have one table that depends upon another table. In the case where a record from a table has a one-to-one correspondence to a record from another table, an entity class for one table should be configured to have a one-to-one correspondence with the entity class for the other table. Working with objects is a bit different from working with database records, but the concept is basically the same. Within the database, a unique identifier is used to correlate one table to another. For instance, in the case of this example, the AUTHOR_DETAIL table contains a field named AUTHOR_ID, and it must contain an ID from the AUTHOR database table in order to map the two records together. Owned entity relationships work a bit differently in that the entity object itself is used to map to another entity, rather than an ID number.

When creating a bidirectional one-to-one relationship between entity classes, each entity class must declare the other entity class as a persistent field or property and then designate the type of relationship using the @OneToOne annotation. The @OneToOne annotation is used to designate a one-to-one relationship between the entities. The @OneToOne annotation contains the following optional attributes:

  • cascade: The operations (e.g., delete) that must be cascaded to the target of the association. Default: no operations.
  • fetch: Whether the association should be lazily loaded or must be eagerly fetched. Default: EAGER.
  • optional: Whether the association is optional. For instance, can the entity be persisted without the association? Default: true.
  • mappedBy: The field that owns the relationship. Default: "".

In the solution to this recipe, the AuthorDetail entity specifies the @OneToOne annotation prior to the declaration of the Author field specifying the mappedBy and optional attributes. The mappedBy attribute is set to authorDetail, because this will be the mapping field, and the optional attribute is set to false. On the other hand, the Author entity specifies the @OneToOne annotation prior to the declaration of the AuthorDetail field, and there are no attributes specified. In practice, when these entities are used, a bidirectional mapping will be enforced. This means that an AuthorDetail object cannot exist without a corresponding Author object.

8-7. Defining One-to-Many and Many-to-One Relationships

Problem

You want to associate two entity classes to each other, such that one entity object can contain a reference to many of the other entity objects.

Solution

Define a relationship between the two entities by specifying the @OneToMany annotation on a field or property referencing the other entity class within the owning object and by specifying the @ManyToOne annotation on a field or property referencing the owning object within the nonowning entity. For instance, let’s say you allow an Author object to contain many different addresses, or AuthorDetail objects. In fact, an Author can contain as many addresses as needed. That being the case, there would be one Author object for every AuthorDetail object. Likewise, there could be many AuthorDetail objects for every Author object.

In the following code listings, I will demonstrate the one-to-many relationship between the Author and AuthorDetail objects. First, let’s take a look at the Author object, which is otherwise referred to as the owning object. This entity class can contain a reference to many different AuthorDetail objects.

@Entity
@Table(name = "AUTHOR")

public class Author implements Serializable {
    
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="author_s_generator",sequenceName="author_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="author_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 30)
    @Column(name = "LAST")
    private String last;
    @Size(max = 30)
    @Column(name = "FIRST")
    private String first;
    @Lob
    @Column(name = "BIO")
    private String bio;
    @OneToMany(mappedBy="author
    private Set<AuthorDetail> authorDetail;

    public Author() {
    }
...

Next, I’ll show the nonowning object, also known as the AuthorDetail class. There may be many AuthorDetail objects within a single Author object.

public class AuthorDetail implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="author_detail_s_generator",sequenceName="author__detail_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="author_detail_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 200)
    @Column(name = "ADDRESS1")
    private String address1;
    @Size(max = 200)
    @Column(name = "ADDRESS2")
    private String address2;
    @Size(max = 250)
    @Column(name = "CITY")
    private String city;
    @Size(max = 2)
    @Column(name = "STATE")
    private String state;
    @Size(max = 10)
    @Column(name = "ZIP")
    private String zip;
    @Column(name = "START_DATE")
    @Temporal(TemporalType.DATE)
    private Date startDate;
    @Lob
    @Column(name = "NOTES")
    private String notes;
    @ManyToOne
    private Author author;

    public AuthorDetail() {
    }
...

image Note   To run the org.javaeerecipes.chapter08.recipe08_07.RecipeTest.java example, please be sure to add both entity classes for this example to the persistence.xml context file. Also, be sure to comment out any other entities within the persistence context by the same name, because there may not be duplicate entities within a single persistence context.

How It Works

The most common database table relationship is the one-to-many or many-to-one relationship, whereby a record in one table may relate to one or more records within another table. Consider the scenario from the solution to this recipe, being that a single AUTHOR table record may have one or more address records within the AUTHOR_DETAIL table. Defining this relationship within the entity classes is easy, because annotations are used to indicate the relationship.

When creating a one-to-many relationship within an entity, the entity that corresponds to the table where one record can correlate to many in another table is known as the owning entity. The entity that correlates to the database table that may contain more than one record relating to the single record in the other table is known as the nonowning entity. The owning entity class should declare a persistent field or property for the entity to which it relates and may have more than one related object. Since there may be more than one nonowning entity object, the owning entity must declare a Set of the nonowning objects and indicate as such using the @OneToMany annotation. The mappedBy attribute of the @OneToMany annotation should be set to the name, which is used within the nonowning entity for declaration of the many-to-one relationship. In the example, the Author entity contains a one-to-many relationship with AuthorDetail. Therefore, the Author entity declares the relationship as follows:

@OneToMany(mappedBy="author")
private Set<AuthorDetail> authorDetail;

On the other end of the spectrum is the many-to-one relationship. In the example, more than one AuthorDetail object may relate to one Author object. Therefore, a many-to-one relationship should be defined within the AuthorDetail entity class for the Author entity. This is done by declaring a persistent field or property for the Author entity and signifying the relationship with the @ManyToOne annotation as follows:

@ManyToOne
private Author author;

When working with the entities, a Set containing one or more AuthorDetail objects should be persisted within a single Author object. The following code demonstrates how to use a one-to-many relationship within an application:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("JavaEERecipesLOCAL");
EntityManager em = emf.createEntityManager();
try {
    em.getTransaction().begin();
    Author author = new Author();
    author.setFirst("JOE");
    author.setLast("TESTER");
    author.setBio("An author test account.");
    Set detailSet = new HashSet<AuthorDetail>();
    AuthorDetail detail = new AuthorDetail();
    detail.setAddress1("Address 1");
    detail.setAddress2("Address 2");
    detail.setCity("NoMansLand");
    detail.setState("ZZ");
    detail.setZip("12345");
    detail.setNotes("This is a test detail");
    detailSet.add(detail);
    AuthorDetail detail2 = new AuthorDetail();
    detail.setAddress1("Address 1");
    detail.setAddress2("Address 2");
    detail.setCity("NoMansLand");
    detail.setState("ZZ");
    detail.setZip("12345");
    detail.setNotes("This is a test detail");
    detailSet.add(detail2);
    em.persist(author);
    em.getTransaction().commit();
} catch (Exception ex){
    System.err.println(ex);
} finally{
            if (em != null){
                em.close();
            }
}

The @OneToMany annotation contains the following optional attributes:

  • cascade: The operations (e.g., delete) that must be cascaded to the target of the association. Default: no operations.
  • fetch: Whether the association should be lazily loaded or must be eagerly fetched. Default: EAGER.
  • orphanRemoval: Whether to apply the remove operation to entities that have been removed from the relationship and to cascade the remove operation to those entities. Default: false.
  • targetedEntity: The entity class that is the target of the association. Default: "".

The @ManyToMany annotation contains the following optional attributes:

  • cascade: The operations (e.g., delete) that must be cascaded to the target of the association. Default: no operations.
  • fetch: Whether the association should be lazily loaded or must be eagerly fetched. Default: EAGER.
  • targetedEntity: The entity class that is the target of the association. Default: "".

8-8. Defining a Many-to-Many Relationship

Problem

There are tables within your database that contain cases where multiple records from one table may correlate to multiple records from another. You want to define entity relationships for these tables.

Solution

Create a many-to-many association between the two tables by declaring a field or property within each entity class for a Set objects corresponding to the entity class on the opposite end. Utilize the @ManyToMany annotation to specify the relationship, and mark the owning side of the relationship by specifying a mappedBy attribute on the nonowning entity’s @ManyToMany annotation. Therefore, the class org.javaeerecipes.chapter08.recipe08_08.Book is the entity class corresponding to the BOOK database table, and it will contain the @ManyToMany annotation on a declaration for a Set of BookAuthor objects. A mapping table in the database will be “automagically” populated with the associated mappings from the entities. Shown next is the partial code for the Book class, the “owning” entity:

@Entity
@Table(name = "BOOK")
@NamedQueries({
    @NamedQuery(name = "Book.findAll", query = "SELECT b FROM Book b"),
})
public class Book implements Serializable {
    
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="book_s_generator",sequenceName="book_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="book_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 150)
    @Column(name = "TITLE")
    private String title;
    @Size(max = 500)
    @Column(name = "IMAGE")
    private String image;
    @Lob
    @Column(name = "DESCRIPTION")
    private String description;
    @ManyToMany
    private Set<BookAuthorMany> bookAuthors;

The BookAuthor class is mapped to the Book class using the same concept. The only difference is that it contains a mappedBy attribute within the @ManyToOne annotation to signify the owning table relation.

@Entity
@Table(name = "BOOK_AUTHOR")
@NamedQueries({
    @NamedQuery(name = "BookAuthor.findAll", query = "SELECT b FROM BookAuthor b"),
    @NamedQuery(name = "BookAuthor.findById", query = "SELECT b FROM BookAuthor b WHERE b.id = :id"),
    @NamedQuery(name = "BookAuthor.findByLast", query = "SELECT b FROM BookAuthor b WHERE b.last = :last"),
    @NamedQuery(name = "BookAuthor.findByFirst", query = "SELECT b FROM BookAuthor b WHERE b.first = :first")})
public class BookAuthorMany implements Serializable {
    
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="book_author_s_generator",sequenceName="book_author_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="book_author_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 30)
    @Column(name = "LAST")
    private String last;
    @Size(max = 30)
    @Column(name = "FIRST")
    private String first;
    @Lob
    @Column(name = "BIO")
    private String bio;
    @ManyToMany(mappedBy="bookAuthors")
    private Set<Book> books;

image Note   The BookAuthor entity has been named BookAuthorMany so that there are no conflicting entity classes within the JavaEERecipes sources. No entities with duplicate names can exist within the same application.

How It Works

It is possible for databases to contain a many-to-many relationship between two or more different tables. In the case of the example in this recipe, a book may have many authors, and an author may have written many books. On that note, both the database table containing books and the database table containing authors are associated to each other via a many-to-many relationship. It is easy to associate entity classes to one another to form a many-to-many relationship via the use of the @ManyToMany annotation. The @ManyToMany annotation is used to signify that an entity contains a many-to-many association with the annotated persistent field or property.

To create the association, each entity within the many-to-many relationship should declare a field or property for a Set of the associated entity objects. In the case of the example, the Book entity should declare a Set of BookAuthor objects, and vice versa. That declaration is then annotated using @ManyToMany, using any attributes that are deemed necessary to the association. The @ManyToMany annotation contains the following optional attributes:

  • targetEntity: The entity class that is the target of the association. This is necessary only if the collection-valued relationship property is not defined using Java generics.
  • cascade: The operations that must be cascaded to the target of the association.
  • fetch: Whether the association should be lazily loaded or eagerly fetched. The default is javax.persistence.FetchType.LAZY.
  • mappedBy: The field that owns the relationship. This is not required if the relationship is unidirectional.

As such, when creating an object of either type, one may persist a Set of the associated entity objects using the persistent field or property that has been annotated with @ManyToMany. The following example demonstrates how to create an entity with a many-to-many relationship (excerpt from the org.javaeerecipes.chapter08.recipe08_08.RecipeTest class):

EntityManagerFactory emf = Persistence.createEntityManagerFactory("JavaEERecipesLOCAL");
EntityManager em = emf.createEntityManager();
try {
    em.getTransaction().begin();
    Book book1 = new Book();
    book1.setTitle("New Book 1");
    Book book2 = new Book();
    book2.setTitle("New Book 2");

    BookAuthorMany author1 = new BookAuthorMany();
    author1.setFirst("JOE");
    author1.setLast("AUTHOR 1");

    BookAuthorMany author2 = new BookAuthorMany();
    author2.setFirst("MARYJJOE");
    author2.setLast("AUTHOR 2");

    Set authors = new HashSet();
    authors.add(author1);
    authors.add(author2);

    Set books = new HashSet();
    books.add(book1);
    books.add(book2);

    book1.setBookAuthor(authors);
    author1.setBooks(books);

    em.persist(author1);
    em.persist(book1);
    em.getTransaction().commit();
} catch (Exception ex){
    System.err.println(ex);
} finally{
            if (em != null){
                em.close();
            }
}

When an entity object that contains a many-to-many association with another is created, a record is populated into a mapping table that contains the primary key from each associated table record. You can optionally specify the name of the mapping table by using the annotation @JoinTable and specifying the name of the table. If no @JoinTable annotation is used, then the mapping table name is derived from a concatenation of the two entity classes, beginning with the owning entity. Therefore, in the example, the mapping table name is BOOK_BOOK_AUTHOR, and it contains a field for storing the primary key from the associated records of each table.

8-9. Querying with Named Queries

Problem

Rather than issue SQL or Java Persistence Query Language (JPQL) queries to a persistence unit, you want to define one or more predefined queries for an entity class that can be called by name.

Solution

Specify a single named query or a group of named queries for an entity class. Provide a name for each of the named queries so that they can be called by that name. In this example, a group of named queries will be added to the BookAuthor entity class, and then a separate class may be used to query the entity class using the named queries. We will create an EntityManagerFactory and database connection based upon a persistence.xml file that obtains a local JDBC connection to the database. The following excerpt is taken from the BookAuthor entity, and it demonstrates how to associate named queries with an entity class:

@Entity
@Table(name = "BOOK_AUTHOR")
@NamedQueries({
    @NamedQuery(name = "BookAuthor.findAll", query = "SELECT b FROM BookAuthor b"),
    @NamedQuery(name = "BookAuthor.findById", query = "SELECT b FROM BookAuthor b WHERE b.id = :id"),
    @NamedQuery(name = "BookAuthor.findByLast", query = "SELECT b FROM BookAuthor b WHERE b.last = :last"),
    @NamedQuery(name = "BookAuthor.findByFirst", query = "SELECT b FROM BookAuthor b WHERE b.first = :first")})
public class BookAuthor implements Serializable {
    
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="book_author_s_generator",sequenceName="book_author_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="book_author_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 30)
    @Column(name = "LAST")
    private String last;
    @Size(max = 30)
    @Column(name = "FIRST")
    private String first;
    @Lob
    @Column(name = "BIO")
    private String bio;

    public BookAuthor() {
    }
...

In another class, the named queries that have been registered with the BookAuthor entity can be called by name. The following excerpt from the org.javaeerecipes.chapter8.recipe8_09.RecipeTest class demonstrates how to invoke a named query:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("JavaEERecipesLOCAL");
EntityManager em = emf.createEntityManager();
try {
    EntityTransaction entr = em.getTransaction();
    entr.begin();
    Query query = em.createNamedQuery("BookAuthor.findAll");
    List authorList = query.getResultList();
    Iterator authorIterator = authorList.iterator();
    while (authorIterator.hasNext()) {
        BookAuthor author = (BookAuthor) authorIterator.next();
        System.out.print("Name:" + author.getFirst() + " " + author.getLast());
        System.out.println();
    }
} catch (Exception ex){
    System.err.println(ex);
}

How It Works

A named query is contained within an entity class, and it consists of a static JPQL query that is specified via metadata. A given entity class can include zero or more named queries or a group of named queries. A named query is expressed via the @NamedQuery annotation, which contains two attributes: name and query. The name attribute of the @NamedQuery annotation is used to specify a String-based name for the query, and the query attribute is used to specify the static JPQL query against the entity. If an entity contains a group of named query annotations, they can be grouped together using the @NamedQueries annotation. One or more @NamedQuery annotation specifications can exist within a single @NamedQueries annotation, separated by commas.

The JPQL within a named query can contain zero or more bind variables that can have values substituted when the named query is called. To utilize a named query, you must first obtain an active connection to the database. To learn more about obtaining an active connection to the database via an EntityManagerFactory, please refer to Recipe 8-3. Once an active database connection has been obtained, the EntityManager object’s createNamedQuery method can be called, passing the string-based name of the named query that you would like to issue. A Query object is returned from the call, and it can be used to obtain the query results.

In the example for this recipe, you can see that the BookAuthor entity is queried, returning a List of BookAuthor objects. A simple while loop is used to iterate through the List of objects, printing the first and last names from each BookAuthor object to System.out (the server log).

8-10. Performing Validation on Entity Fields

Problem

You want to specify validation rules for specific fields within an entity class to prevent invalid data from being inserted into the database.

Solution

Include bean validation constraints within an entity class. Bean validation constraints are annotations that are applied to persistent fields or properties of an entity class. The bean validation mechanism provides a number of annotations that can be placed on fields or properties in order to validate data in different ways. In the following example, the AuthorWork entity has been enhanced to include bean validation for the id, address1, state, and zip fields.

...
@Entity
@Table(name = "AUTHOR_DETAIL")

public class AuthorDetailBeanValidation implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @SequenceGenerator(name="author_detail_s_generator",sequenceName="author__detail_s", initialValue=1, allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE,
    generator="author_detail_s_generator")
    @NotNull
    @Column(name = "ID")
    private BigDecimal id;
    @Size(max = 200)
    @Pattern(regexp="", message="Invalid Address")
    @Column(name = "ADDRESS1")
    private String address1;
    @Size(max = 200)
    @Column(name = "ADDRESS2")
    private String address2;
    @Size(max = 250)
    @Column(name = "CITY")
    private String city;
    @Size(max = 2)
    @Column(name = "STATE")
    @Pattern(regexp="^(?-i:A[LKSZRAEP]|C[AOT]|D[EC]|F[LM]|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEHINOPST]|N[CDEHJMVY]|O[HKR]|P[ARW]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$",
            message="Invalid State")
    private String state;
    @Size(max = 10)
    @Column(name = "ZIP")
    @Pattern(regexp="^\d{5}\p{Punct}?\s?(?:\d{4})?$",
            message="Invalid Zip Code")
    private String zip;
    @Column(name = "START_DATE")
    @Temporal(TemporalType.DATE)
    private Date startDate;
    @Lob
    @Column(name = "NOTES")
    private String notes;
    @ManyToOne
    private AuthorBeanValidation author;
...

In an attempt to insert a value that does not conform to the validation rules, the object will not be persisted, and the message correlating to the bean validation annotation will be displayed.

How It Works

It is always a good idea to utilize a data validation strategy when working with user input, especially if the data will be persisted into a database or other data store for later use. The Java Persistence API allows bean validation to occur within an entity class, whereby a developer can place validation rules directly on a persistent field or property. By default, the persistence provider automatically invokes validation processes on entities containing bean validation annotation constraints after the PrePersist, PreUpdate, and PreRemove life-cycle events occur. At that time, any value that does not adhere to the given validation constraint will cause the entity to stop persistence and display an associated message.

The details of bean validation are the same, whether it be on a plain old Java object (POJO) or an entity class. In the case of an entity class, either the persistent field or property can be annotated with the desired bean validation constraint. To see a list of possible bean validation constraint annotations, please refer to Table 5-8 in Chapter 5.

In the example for this recipe, the @NotNull and @Pattern annotations are specified on persistent properties of the AuthorDetail entity. Specifically, the id field is annotated with @NotNull, and validation will fail in an attempt to enter a NULL value for that field. The state and zip fields contain a @Pattern annotation, along with a corresponding regular expression and failure message. If the values for those fields do not adhere to the regular expression that has been specified, then the message that is assigned to the message attribute of the @Pattern annotation will be displayed via a JSF view by the h:message component corresponding to the validated field. What if you want to apply a set of regular expression patterns to a given field or property? Such a feat can be done using the @Pattern.List syntax, whereby the list would contain a comma-separated list of @Pattern annotations. The following lines of code demonstrate this technique:

@Pattern.List({
    @Pattern(regexp="regex-pattern", message="Error Message"),
    @Pattern(regexp="another regex-pattern", message("Error Message 2")
})

Bean validation is a good way to ensure that invalid data is not submitted to a data store. However, most advanced desktop or web applications today use a couple tiers of validation to make the user experience more convenient. Many times, web applications use JavaScript field validation first so that users do not have to submit a page in order to see their validation errors displayed on the screen. If using JSF or other web frameworks, some components allow direct access to bean validation, in which cases an Ajax submission of a given field or property will occur behind the scenes, allowing the bean validation to take place without page submission. Whatever tact you take, bean validation within entity classes is important and should become a handy tool to add to your arsenal.

8-11. Generating Database Schema Objects Automatically

Problem

You are developing an application and want to automatically have your entity classes generated into tables within the underlying database.

Solution

Use the automatic schema generation that was introduced in EJB 3.2. Schema generation is determined by the object-relational metadata of the persistence.xml unit, unless custom scripts are provided for the generation. The application developer can package scripts as part of the persistence unit or can supply URLs to the location of the scripts for schema generation. The execution of such scripts can be carried out by the container itself, or the container may direct the persistence provider to take care of script execution. Table 8-3 in the “How it Works” section of this recipe lists the different persistence.xml or EntityManagerFactory properties that are used to configure schema generation. These properties are passed as a Map argument from the container to the PersistenceProvider generateSchema method or the createContainerEntityManagerFactory method.

To define the different objects that need to be generated, annotate entity classes accordingly. The standard entity class annotations (@Table, @Id, and so on) determine what objects are created and how they are structured. For more information regarding the specification of annotations within entity classes in order to generate schema objects, please refer to the annotations listed in Table 8-4 within the “How It Works” section of this recipe.

How It Works

Schema generation refers to the creation of underlying database tables, views, constraints, and other database artifacts. Prior to the Java EE 7 release, schema generation has been automated only via the use of an IDE such as NetBeans or Eclipse. However, the EE 7 release takes a step toward breaking this dependency on an IDE by allowing schema generation to become automated by configuring an appropriate persistence.xml file for an application.

Schema generation can be applied directly to the database, or it can generate SQL scripts that can be manually applied to the database (or both), depending upon which options are configured for the application. Schema generation may occur prior to application deployment or when an EntityManagerFactory is created as part of the application deployment and initialization. To perform schema generation, the container may call the PersistenceProvider generateSchema method separately from and/or prior to the entity manager factory for the persistence unit. The createContainerEntityManagerFactory call can accept additional information to cause the generation of schema constructs to occur as part of the entity manager factory creation or initialization process. Furthermore, this information can determine whether the database is manipulated directly or whether SQL scripts are created, or both.

image Note   Schema generation is also available outside of a managed container (e.g., web application server) in Java SE environments. To perform schema generation in this environment, the application may call the Persistence generateSchema method separately from and/or prior to the creation of the entity manager factory or may pass information to the createEntityManagerFactory method to cause schema generation to occur as part of the entity manager factory creation.

Table 8-3 lists the different schema generation properties that can be specified in the persistence.xml file in order to automate schema generation.

Table 8-3. Schema Generation Properties

Property Purpose
schema-generation-action Controls the action to be taken by persistence provider with regards to object generation and destructiom. Values: none, create, drop-and-create, drop.
schema-generation-target Controls whether schema is to be created within the database, whether DDL scripts are to be created, or both. Values: database, scripts, database-and-scripts.
ddl-create-script-target, ddl-drop-script-target Controls target locations for writing scripts if the schema-generation-target specifies script generation. Writers are preconfigured for the persistence provider. Values: java.io.Writer (e.g., MyWriter.class) or URL strings.
ddl-create-script-source,  ddl-drop-script-source Specifies locations from which DDL scripts are to be read. Readers are preconfigured for the persistence provider. Values: java.io.Reader (e.g., MyReader.class) or URL strings.
sql-load-script-source Specifies the file location of SQL bulk load script. Values: java.io.Reader (e.g., MyReader.class) or URL string.
schema-generation-connection JDBC connection to be used for performing schema generation.
database-product-name, database-major-version, database-minor-version Needed if scripts are to be generated. Values are those obtained from JDBC DatabaseMetaData.
create-database-schemas Whether the persistence provider needs to create schema in addition to creating database objects such as tables, sequences, constraints, and so on. Values: true, false.

Programmatically, schema generation is determined by a series of annotations that are placed in entity classes. The @Table annotation denotes an entity mapping to an underlying database table. By default, a table is generated for each top-level entity and includes columns based upon the specified attributes for that entity. Therefore, the @Column and @JoinColumn annotations are used for generating such columns for a table. Column ordering is not determined based upon the ordering of @Column or @JoinColumn annotations. If column ordering is important, then a Data Definition Language (DDL) script must be supplied for generating the table. Other annotations and annotation attributes, such as @Id, also play important roles in schema generation. Table 8-4 lists the different annotations that are involved in schema generation, along with a brief description and the elements that can be populated for further control over the generated schema.

Table 8-4. Schema Generation Annotations

Annotation Description Elements
@Table Used for generating tables. By default, the table name is generated from the entity name, and the entity name is defaulted from the class name.
@SecondaryTable A secondary table is created to partition the mapping of entity state across multiple tables.
@CollectionTable A collection table is created for mapping of an element collection. The Column, AttributeOverride, and AssociationOverride annotations may be used to override CollectionTable mappings.
@JoinTable Used in mapping of associations.  By default, join tables are created for the mapping of many-to-many relationships and unidirectional one-to-many relationships.
@TableGenerator Used to store generated primary key values.
@Column Determines the name and configuration for a column within a table. unique, nullable, columnDefinition,  table,  length,  precision,  scale,  name
@MapKeyColumn Specifies the mapping name of a key column of a map when the key is of basic type. unique,  nullable,  columnDefinition,  table,  length,  precision,  scale
@Enumerated, @MapKeyEnumerated Controls whether string- or integer-valued columns are generated for basic attributes of enumerated types and therefore impact the default column mapping of these types.
@Temporal, @MapKeyTemporal Controls whether date-, time-, or timestamp-value columns are generated for basic attributes of temporal types and therefore impact the default column mappings for these types.
@Lob Specifies that a persistent attribute is to be mapped to a database large object type.
@OrderColumn Specifies the generation of a column that is used to maintain the persistent ordering of a list that is represented in an element collection, one-to-many, or many-to-many relationship. name, nullable, columnDefinition
@DiscriminatorColumn Generated for the SINGLE_TABLE mapping strategy and may optionally be generated by the provider for use with the JOINED inheritance strategy.
@Version Specifies the generation of a column to serve as an entity’s optimistic lock.
@Id Specifies a database primary key column . Use of the @Id annotation results in the creation of a primary key which consists of the corresponding column or columns.
@EmbeddedId Specifies an embedded attribute whose corresponding columns formulate a database primary key. Use of the @EmbeddedId annotation results in the creation of a primary key consisting of the corresponding columns.
@GeneratedValue Indicates a primary key that should have an automatically generated value. If a strategy is indicated, the provider must use it if it is supported by the target database.
@JoinColumn The @JoinColumn annotation is typically used for specifying a foreign key mapping. name, referencedColumnName, unique, nullable, columnDefinition, table, foreignKey
@MapKeyJoinColumn Specifies foreign key mappings to entities that are map keys in element collections or relationships that consist of map values. name, referencedColumnName,  unique, nullable, columnDefinition, table, foreignKey
@PrimaryJoinKeyColumn Specifies that a primary key column is to be used as a foreign key. This annotation is used in the specification of the JOINED mapping strategy and for joining a secondary table to a primary table in a one-to-one relationship mapping.
@ForeignKey Used within the JoinColumn, JoinColumns, MapKeyJoinColumn, MapKeyJoinColumns, PrimaryKeyJoinColumn, and PrimaryKeyJoinColumns annotations to specify or override a foreign key constraint.
@SequenceGenerator Creates a database sequence to be used for ID generation.
@Index Generates an index consisting of the specified columns.
@UniqueConstraint Generates a unique constraint for the given table.

As per Table 8-4, there are a couple of new annotations that have been created specifically to facilitate schema generation. The new annotations are @Index and @ForeignKey, where @Index is responsible for generating an index of the specified columns. @ForeignKey is used to define a foreign key on a table.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.35.255