What’s New in JPA

Version 4

    What’s New in JPA

     

    by Josh Juneau

     

    Attribute conversion, schema generation, enhanced SQL queries, and a host of other improvements highlight JPA 2.1.

     

    Published May/June 2015

     

    The Java Persistence API (JPA) is a fundamental component for relational database–driven Java EE applications. Although JPA 2.1 was a minor release, some of the new features that were added pack a punch, and they can vastly improve overall developer productivity.

     

    This article covers some of the latest features in JPA 2.1, which was introduced with Java EE 7. Throughout the article, we will delve into the new features of JPA 2.1, examining examples of each in a real-world application, so that you can begin applying these new features to your Java EE applications today.

     

    JPA 2.0 Schema

    Automatic schema generation is achieved in JPA 2.1 via a combination of annotations and by specifying properties.

     

    The AcmePools application we will use for the examples in this article was developed using Java EE 7 with JavaServer Faces (JSF) for the front end and using Enterprise JavaBeans (EJB) beans for working with data. The application was originally built in an article entitled “PrimeFaces in the Enterprise,” which was published on Oracle Technology Network.

     

    Database Schema and Type Mapping

     

    Developers face several challenges when modeling database tables with Java objects. JPA 2.1 brings various enhancements in database schema and type mapping, helping to ease the transition between a relational database and Java objects. This section covers two of the new features that help in this area: attribute conversion and schema generation.

     

    Attribute conversion. In order to represent database tables as objects, you must map each database type to a Java type, aka type mapping. For some cases, JPA does not automatically convert from a Java type to a database type or vice versa. For instance, perhaps JPA contains a specified mapping, and an application requires something different. JPA 2.1 allows you to write an attribute converter in such cases, to specify which database type should correspond to a specified Java type.

     

    Similar to a custom Hibernate type, attribute conversion can be applied to a basic attribute or to an element of a collection type in order to convert between a database attribute (column) type and a Java object type. For instance, in the example application, we would rather see a String value of N or Y in the database than a 0 or 1 for the representation of a Java Boolean value. We can code a converter into the application to convert a Java false value to an N and a Java true value to a Y, and then store the values in VARCHAR fields. To do so, you must create a converter class, which implements the javax.persistence.AttributeConverter inter-face. This interface accepts an X and a Y value, with X being the Java type and Y being the corresponding database type.

     

    A type converter should not be applied to Id attributes, version attributes, relationship attributes, or attributes denoted as Enumerated or Temporal, because doing so makes an application nonportable. Listing 1 demonstrates a converter for Boolean-to-String conversion.

     

    @Converter public class BooleanToCharacterConverter implements     AttributeConverter {      @Override     public String convertToDatabaseColumn(Boolean x) {                  if(x){             return "Y";         } else {             return "N";         }     }      @Override     public Boolean convertToEntityAttribute(String y) {         String val = (String) y;         if(val.equals("Y")){             return true;         } else {             return false;         }     }      }

     

    Listing 1

     

    A converter can be applied automatically, or it can be applied using the @Convert annotation, specifying the class name of the converter. In the example application, the @Convert annotation is specified on the entity class attribute that should use the converter when being persisted or retrieved, as seen in Listing 2.

     

    public class Customer implements Serializable {     private static final long serialVersionUID = 1L; @Id     @Basic(optional = false)     @NotNull     @Column(name = "CUSTOMER_ID")     private Integer customerId; ...     @OneToMany(cascade = CascadeType.ALL,                mappedBy = "customerId")     private Collection jobCollection;     @Column(name="CURRENT_MAINTENANCE")     @Convert(converter=BooleanToCharacterConverter.class)     private boolean currentMaintenance; ...

     

    Listing 2

     

    Attribute conversion makes it possible to easily configure a custom mapping between a database field type and a Java type without muddying up entity classes with conversion logic.

     

    Schema generation. In the past, developers relied upon manual generation of entity classes for a given database, or they utilized a nonstandard method of autogeneration, such as an IDE or provider-specific implementation. Manual generation of entity classes can be cumbersome and error-prone, and the use of an IDE or provider-specific implementation can cause vendor lock-in. These issues have been resolved in JPA 2.1 via the introduction of automatic schema generation. Automatic schema generation is achieved in JPA 2.1 via a combination of annotations and also by specifying properties within the persistence context of an application.

     

    An annotation was introduced with the release of JPA 2.1, providing a more convenient way to invoke procedures stored in a database.

     

    To accommodate schema generation, properties can be added to the persistence.xml file to designate how to perform the schema generation. The properties shown in Listing 3 are some of the most often used, which can be specified to generate a database schema. For a complete listing of the different properties, see the Java EE 7 tutorial.

     

    javax.persistence.schema-generation.database.action javax.persistence.schema-generation.create-source javax.persistence.schema-generation.drop-source javax.persistence.sql-load-script-source

     

    Listing 3

     

    Listing 4 demonstrates an example of a persistence.xml file containing JPA schema-generation properties. The properties that designate a script source point to SQL scripts that contain the SQL code that is required to create, load, or delete the database objects. There is also an API for programmatically generating a database schema. Oftentimes, these scripts are placed within the application META-INF folder.

     

    <persistence-unit name="postpu" transaction-type="JTA">    <properties>       <property name="javax.persistence.schema-generation             .database.action" value="drop-and-create"/>       <property name="javax.persistence.schema-generation             .create-source" value="metadata"/>       <property name="javax.persistence.schema-generation             .drop-source" value="metadata"/>       <property name="javax.persistence.schema-generation             .drop-script-source"              value="META-INF/drop-script.sql"/>       <property name="javax.persistence.schema-generation             .create-script-source"              value="META-INF/create-script.sql"/>       <property name="javax.persistence             .sql-load-script-source"              value="META-INF/load-script.sql"/>       <property name="eclipselink.logging.level"              value="FINE"/>     </properties> </persistence-unit>

     

    Listing 4

     

    Another benefit that JPA schema generation has over most IDE- and provider-specific implementations is that it provides support for indexes and foreign keys via the @Index and @ForeignKey annotations, respectively. To designate an index on a database attribute, specify the indexes attribute of the @Table annotation, and then list each index within the indexes attribute using @Index.

     

    Listing 5 shows how to specify an index on the Customer entity. The @ForeignKey annotation is placed within the @JoinColumn annotation, which is specified on an entity attribute (see Listing 6). Note that if it is specified, the @ForeignKey annotation overrides the defaults provided by the persistence provider.

     

    @Entity @Table(name = "CUSTOMER",indexes={     @Index(columnList="CURRENT_MAINTENANCE") }) @XmlRootElement ... public class Customer implements Serializable { ...

     

    Listing 5

     

    ... @JoinColumn(foreignKey=   @ForeignKey(foreignKeyDefinition=     "FOREIGN KEY (POOL_ID) REFERENCES POOL")) @ManyToOne private Pool pool; ...

     

    Listing 6

     

    Read the full article in Java Magazine, May/June edition at oracle.com/javamagazine

     

    About the Author

     

    juneau-headshot.jpg

     

    Josh Juneau is an application developer, system analyst, and DBA. He primarily develops using Java and other Java Virtual Machine (JVM) languages. Juneau is a technical writer for Oracle Technology Network and Java Magazine. He wrote Java EE 7 Recipes and Introducing Java EE 7 (both Apress, 2013) and Java 8 Recipes (Apress, 2014).