Basic Relational Mapping




Basic Relational Mapping

A developer can take two directions when implementing entity beans. Some applications start from a Java object model and derive a database schema from this model. Other applications have an existing database schema from which they have to derive a Java object model.

The Java Persistence specification provides enough flexibility to start from either direction. If you are creating a database schema from a Java object model, most persistence vendors have tools that can autogenerate database schemas based on the annotations or XML metadata you provide in your code. In this scenario, prototyping your application is fast and easy, as you do not have to define much metadata in order for the persistence engine to generate a schema for you. When you want to fine-tune your mappings, the Java Persistence specification has the necessary annotations and XML mappings to do this.

If you have an existing database schema, many vendors have tools that can generate Java entity code directly from it. Sometimes, though, this generated code is not very object-oriented and doesn't map to your database very well. Luckily, the Java Persistence specification provides the necessary mapping capabilities to facilitate this problem.

You will find that your use of annotations and mapping XML will depend on the direction you are coming from. If you are autogenerating your schema from your entity classes, you will probably not have a need for annotations like @Table and @Column, as you will rely on well-defined specification defaults. If you have an existing schema, you will find that a lot more metadata will need to be specified.

Elementary Schema Mappings

We don't like the default table and column mappings of our original Customer entity class. Either we have an existing table we want to map to, or our DBA is forcing some naming conventions on us. Let's actually define the relational table we want to map our Customer entity to and use the @javax.persistence.Table and @javax. persistence.Column annotations to apply the mapping. Here's the table definition in SQL:

create table CUSTOMER_TABLE
(
    CUST_ID integer primary key not null,
    FIRST_NAME varchar(20) not null
    lastName varchar(255) not null,
);

We want to change the table name and the column names of the id and firstName properties. We also want firstName to have a not-null constraint and want to set the VARCHAR length to 20. Let's modify our original Customer entity class and add the mapping annotations:

package com.titan.domain;

import javax.persistence.*;

@Entity
@Table 
 

(name="CUSTOMER_TABLE")
public class Customer implements java.io.Serializable {
   private long id;
   private String firstName;
   private String lastName;

   @Id
@Column(name="CUST_ID", nullable=false, columnDefinition="integer")
   public long getId( ) { return id; }
   public void setId(long id) { this.id = id; }

@Column(name="FIRST_NAME", length=20, nullable=false)
   public String getFirstName( ) { return firstName; }
   public void setFirstName(String firstName) { this.firstName = firstName; }

   public String getLastName( ) { return lastName; }
   public void setLastName(String lastName) { this.lastName = lastName; }
}

@Table

The @javax.persistence.Table annotation tells the EntityManager service the relational table your bean class maps to. You do not have to specify this annotation if you do not want to, as, again, the table name defaults to the unqualified class name of the bean. Let's look at the full definition of this annotation:

package javax.persistence;

@Target({TYPE}) @Retention(RUNTIME)
public @interface Table
{
   String name( ) default "";
   String catalog( ) default "";
   String schema( ) default "";
   UniqueConstraint 
 uniqueConstraints( ) default {};
}

The catalog( ) and schema( ) attributes are self-explanatory, as they identify the relational catalog and schema the table belongs to:

public @interface UniqueConstraint
{
   String[] columnNames( );
}

The @Table.uniqueConstraints( ) attribute allows you to specify unique column constraints that should be included in a generated Data Definition Language (DDL). Some vendors have tools that can create DDLs from a set of Entity classes or even provide automatic table generation when a bean is deployed. The UniqueConstraint annotation is useful for defining additional constraints when using these specific vendor features. If you are not using the schema generation tools provided by your vendor, then you will not need to define this piece of metadata.

The @Table and @UniqueConstraint annotations have an XML equivalent in the mapping file schema:

<table name="CUSTOMER_TABLE" catalog="TITAN" schema="TITAN">
   <unique-constraint>
      <column-name>SOME_OTHER_ATTRIBUTE</column_name>
   </unique-constraint>
</table>

The <table> element is a subelement of <entity>. You can specify as many <unique-constraint> elements as you need to. A unique constraint can contain several columns as well to represent multicolumn unique constraints.

@Column

Using the @Column annotation, we set the id property's column name to be CUST_ID and not nullable, and for its database type to be an integer. For the firstName property, we also changed its column name and set the VARCHAR length it will be mapped to 20.

The @javax.persistence.Column annotation describes how a particular field or property is mapped to a specific column in a table:

public @interface Column
{
   String name( ) default "";
   boolean unique( ) default false;
   boolean nullable( ) default true;
   boolean insertable( ) default true;
   boolean updatable( ) default true;
   String columnDefinition( ) default "";
   String table( ) default "";
   int length( ) default 255;
   int precision( ) default 0;
   int scale( ) default 0;
}

The name( ) attribute obviously specifies the column name. If it is unspecified, the column name defaults to the property or field name. The table( ) attribute is used for multitable mappings, which we'll cover later in this chapter. The rest of the attributes are used when you are autogenerating the schema from vendor-provided tools. If you are mapping to an existing schema, you do not need to define any of these attributes. The unique( ) and nullable( ) attributes define constraints you want placed on the column. You can specify whether you want this column to be included in SQL INSERT or UPDATE by using insertable( ) and updatable( ), respectively. The columnDefinition( ) attribute allows you to define the exact DDL used to define the column type. The length( ) attribute determines the length of a VARCHAR when you have a String property. For numeric properties, you can define the scale( ) and precision( ) attributes.

The @Column annotation has the XML equivalent in the <column> element. This element is a subelement of the attribute mapping types <id>, <basic>, <temporal>, <lob>, and <enumerated> that are described later in this chapter.

<basic name="lastName">
   <column name=""
           unique="true"
           nullable="true"
           insertable="true"
           updatable="true"
           column-definition=""
           table=""
           length=""
           precision=""
           scale=""
   />
</basic>

The attributes of the <column> element have the same behavior and default values as their annotation counterparts.

XML

Let's take this entire mapping example and see how it maps in XML:

<entity-mappings>
   <entity class="com.titan.domain.Customer" access="PROPERTY">
      <table name="CUSTOMER_TABLE"/>
      <attributes>
         <id name="id">
            <column name="CUST_ID"
                    nullable="false"
                    column-definition="integer"/>
         </id>
         <basic name="firstName">
            <column name="FIRST_NAME"
                    nullable="false"
                    length="20"/>
         </basic>
      </attributes>
   </entity>
</entity-mappings>