Mapping Persistent Objects (Enterprise JavaBeans 3.1)

 

In this topic, we take a thorough look at the process of developing entity beans— specifically, mapping them to a relational database. A good rule of thumb is that entity beans model business concepts that can be expressed as nouns. Although this is a guideline rather than a requirement, it helps determine when a business concept is a candidate for implementation as an entity bean. In grammar school, you learned that nouns are words that describe a person, place, or thing. The concepts of “person” and “place” are fairly obvious: a person entity might represent a customer or passenger, and a place entity might represent a city or port of call. Similarly, entity beans often represent “things”: real-world objects, such as ships and credit cards, and abstractions, such as reservations. Entity beans describe both the state and behavior of real-world objects and allow developers to encapsulate the data and business rules associated with specific concepts; an Employee entity encapsulates the data and business rules associated with an employee, for example. This makes it possible for data associated with a concept to be manipulated consistently and safely.

Entities represent data in the database, so changes to an entity bean result in changes to the database. That’s ultimately the purpose of an entity bean: to provide programmers with a simpler mechanism for accessing and changing data. It is much easier to change a customer’s name by calling Employee.setName() than by executing an SQL command against the database. In addition, using entity beans provides opportunities for software reuse. Once an entity bean has been defined, its definition can be used throughout your application in a consistent manner. The concept of an employee, for example, is used in many areas of business, including booking, task assignment, and accounts payable. An Employee entity is also a unified model to information and thus ensures that access to its data is consistent and simple. Representing data as entity beans can make development easier and more cost-effective.

When a new entity is created and persisted into the entity manager service, a new record must be inserted into the database and a bean instance must be associated with that data. As the entity is used and its state changes, these changes must be synchronized with the data in the database: entries must be inserted, updated, and removed. The process of coordinating the data represented by a bean instance with the database is called persistence.

The Java Persistence specification gave a complete overhaul to entity beans. CMP 2.1 had a huge weakness in that applications written to that specification were completely nonportable between vendors because there was no object-to-relational (O/R) mapping. O/R mapping was completely left to the vendor’s discretion. These next topics focus solely on Java Persistence’s object mappings to a relational database. This topic focuses on basic entity bean mappings to a relational database.


The Programming Model

Entities are plain Java classes in Java Persistence. You declare and allocate these bean classes just as you would any other plain Java object. You interact with the entity manager service to persist, update, remove, locate, and query for entity beans. The entity manager service is responsible for automatically managing the entity beans’ state. This service takes care of enrolling the entity bean in transactions and persisting its state to the database. We’ve seen this power in the previous topic.

The Employee Entity

The Employee class is a simple entity bean that models the concept of an employee within a company. Java Persistence is all about relational databases. This section introduces the Employee entity’s design and implementation. This entity will be refactored in many different ways throughout this topic so that we can show you the multiple ways in which you can map the entity to a relational database.

The Bean Class

The Employee bean class is a plain Java object that you map to your relational database. It has fields that hold state and, optionally, it has getter and setter methods to access this state. It must have, at minimum, a no-argument constructor (which may be the default, implicit constructor):

tmp95201_thumb_thumbtmp95202_thumb2_thumb3

Java Persistence requires only two pieces of metadata when you are creating a persistent class: the @javax.persistence.Entity annotation denotes that the class should be mapped to your database, and the @javax.persistence.Id annotation marks which property in your class will be used as the primary key. The persistence provider will assume that all other properties in your class will map to a column of the same name and of the same type. The table name will default to the unqualified name of the bean class. Here is the table definition the persistence provider is assuming you are mapping to:

tmp95203_thumb1_thumb1

The @Entity annotation has one name() attribute. This name is used to reference the entity within a JPA QL expression. If you do not provide a value for this attribute, the name defaults to the unqualified name of the bean class.

How you apply the @javax.persistence.Id annotation determines whether you will use the Java bean style for declaring your persistent properties or whether you will use Java fields. If you place the @Id annotation on a getter method, then you must apply any other mapping annotations on getter and setter methods in the class. The provider will also assume that any other getter and setter methods in your class represent persistent properties and will automatically map them based on their base name and type.

Earlier we placed the @Id annotation on a member field of the class. The persistence provider will also assume that any other member fields of the class are also persistent properties and will automatically map them based on their base name and type. Any mapping annotations must be placed on member fields in this example, not on getter or setter methods. Here, we are really defining the access type—that is, whether our relational mappings are defined on the fields or the methods of a class.

XML Mapping File

If you do not want to use annotations to identify and map your entity beans, you can alternatively use an XML mapping file to declare this metadata. By default, the persistence provider will look in the META-INF directory for a file named orm.xml, or you can declare the mapping file in the <mapping-file> element in the persistence.xml deployment descriptor.

The mapping file has a top element of <entity-mappings>. The <entity> element defines the entity class and access type: PROPERTY or FIELD. The <id> element is a subelement of the <attributes> element and defines what attribute your primary key is. Like annotated classes, the persistence provider will assume that any other property in your class is a persistent property, and you do not have to explicitly define them.

For brevity, we’ll continue to use the annotation-based approach in our examples.

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 a solution to 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 probably will not need annotations such as @Table and @Column (covered in this topic), as you will rely on well-defined specification defaults. If you have an existing schema or need to fine-tune the mapping, you may find that more metadata will need to be specified.

Elementary Schema Mappings

Let’s assume we don’t like the default table and column mappings of our original Employee 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 Employee entity to and use the @javax.persistence.Table and @javax.persistence.Column annotations to apply the mapping.

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

tmp95204_thumb_thumbtmp95205_thumb_thumb

@Table

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

tmp95206_thumb_thumb

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

tmp95207_thumb_thumb

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.

@Column

Using the @Column annotation, we set the name property’s column name to be employee_name and not nullable, and we set its database type to be an integer. We also set the VARCHAR length to 20. This is often important to save RAM in the database and keep things moving efficiently; if you don’t need the extra space, it’s a good idea to restrict your field types to take up the least amount of data as possible.

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

tmp95208_thumb_thumb

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 cover later in this topic. 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.

Primary Keys

A primary key is the identity of a given entity bean. Every entity bean must have a primary key, and it must be unique. Primary keys can map to one or more properties and must map to one of the following types: any Java primitive type (including wrappers), java.lang.String, or a primary-key class composed of primitives and/or strings. Let’s first focus on simple one-property primary keys.

@Id

The @javax.persistence.Id annotation identifies one or more properties that make up the primary key for your table:

tmp95209_thumb_thumbtmp95210_thumb2

You can generate the primary key for your entity beans manually or have the persistence provider do it for you. When you want provider-generated keys, you have to use the

tmp95211_thumb_thumb

Persistence providers are required to provide key generation for primitive primary keys. You can define the type of primary generator you would like to have using the strategy() attribute. The GeneratorType.AUTO strategy is the most commonly used configuration, and it is the default:

tmp95212_thumb_thumb

Table Generators

The TABLE strategy designates a user-defined relational table from which the numeric keys will be generated. A relational table with the following logical structure is used:

tmp95213_thumb_thumb

The PRIMARY_KEY_COLUMN holds a value that is used to match the primary key you are generating for. The VALUE_COLUMN holds the value of the counter.

To use this strategy, you must have already defined a table generator using the @javax.persistence.TableGenerator annotation. This annotation can be applied to a class or to the method or field of the primary key:

tmp95214_thumb_thumbtmp95215_thumb_thumb

The name() attribute defines the name of the @TableGenerator and is the name referenced in the @Id.generator() attribute. The table(), catalog(), and schema() attributes describe the table definition of the generator table. The pkColumnName() attribute is the name of the column that identifies the specific entity primary key you are generating for. The valueColumnName() attribute specifies the name of the column that will hold the counter for the generated primary key. pkColumnValue() is the value used to match up with the primary key you are generating for. The allocationSize() attribute is how much the counter will be incremented when the persistence provider queries the table for a new value. This allows the provider to cache blocks so that it doesn’t have to go to the database every time it needs a new ID. If you are autogenerating this table, then you can also define some constraints using the uniqueConstraints() attribute.

Let’s look at how you would actually use this generator on the Employee entity:

tmp95216_thumb_thumb

Now if you allocate and persist() an Employee entity, the id property will be autogen-erated when the persist() operation is called.

Sequence Generators

Some RDBMs, specifically Oracle, have an efficient built-in structure to generate IDs sequentially. This is the SEQUENCE generator strategy. This generator type is declared via the @javax.persistence.SequenceGenerator:

tmp95217_thumb_thumbtmp95218_thumb_thumb

The name() attribute specifies how this @SequenceGenerator is referenced in @Id annotations. Use the sequenceName() attribute to define what sequence table will be used from the database. initialValue() is the first value that will be used for a primary key, and allocationSize() is how much it will be incremented when it is accessed. schema() and catalog(), like their counterparts in @Table, refer to the schema and catalog of the sequence generator, respectively. Let’s again look at applying the SEQUENCE strategy on our Employee entity bean:

tmp95219_thumb1_thumb

This example is a little different from our TABLE strategy example in that the generator is declared on the bean’s class instead of directly on the property. TABLE and SEQUENCE generators can be defined in either place. As with the TABLE generation type, the primary key is autogenerated when the EntityManager.persist() operation is performed.

Primary-Key Classes and Composite Keys

Sometimes relational mappings require a primary key to be composed of multiple persistent properties. For instance, let’s say that our relational model specified that our Employee entity should be identified by both its last name and its Social Security number instead of an autogenerated numeric key. These are called composite keys. The Java Persistence specification provides multiple ways to map this type of model. One is through the @javax.persistence.IdClass annotation; the other is through the @javax.persistence.EmbeddedId annotation.

@IdClass

The first way to define a primary-key class (and, for that matter, composite keys) is to use the @IdClass annotation. Your bean class does not use this primary-key class internally, but it does use it to interact with the entity manager when finding a persisted object through its primary key. @IdClass is a class-level annotation and specifies what primary-key class you should use when interacting with the entity manager.

tmp95220_thumb_thumb

In your bean class, you designate one or more properties that make up your primary key, using the @Id annotation. These properties must map exactly to properties in the @IdClass. Let’s look at changing our Employee bean class to have a composite key made up of last name and Social Security number. First, let’s define our primary-key class:

tmp95221_thumb1_thumb

The primary-key class must meet these requirements:

• It must be Serializable.

• It must have a public no-arg constructor.

• It must implement the equals() and hashCode() methods.

Our Employee bean must have the same exact properties as the ExternalEmployeePK class, and these properties are annotated with multiple @Id annotations:

tmp95222_thumb_thumbtmp95223_thumb2

Primary-key autogeneration is not supported for composite keys and primary-key classes. You will have to manually create the key values in code.

The primary-key class is used whenever you are querying for the Employee:

tmp95225_thumb_thumb

Whenever you call an EntityManager method such as find() or getReference(), you must use the primary-key class to identify the entity.

@EmbeddedId

A different way to define primary-key classes and composite keys is to embed the primary-key class directly in your bean class. The @javax.persistence.EmbeddedId annotation is used for this purpose in conjunction with the @javax.persistence.Embeddable annotation:

tmp95226_thumb_thumb

When we use an @Embeddable class as the type for our primary key, we mark the property as @EmbeddedId. Let’s first see an example of our embeddable primary key type:

tmp95227_thumb_thumbtmp95228_thumb_thumb

We then change our Employee bean class to use the EmbeddedEmployeePK directly, using the @EmbeddedId annotation:

 

tmp95229_thumb_thumb

The EmbeddedEmployeePK primary-key class is used whenever you are fetching the Employee using EntityManager APIs:

 

tmp95230_thumb_thumb

Whenever you call an EntityManager method such as find() or getReference(), you must use the primary-key class to identify the entity.

If you do not want to have the @Column mappings with the primary-key class, or you just want to override them, you can use @AttributeOverrides to declare them directly in your bean class:

tmp95231_thumb1_thumbtmp95232_thumb2

The @AttributeOverrides annotation is an array list of @AttributeOverride annotations. The name() attribute specifies the property name in the embedded class you are mapping to. The column() attribute allows you to describe the column the property maps to.

Property Mappings

So far, we have only shown how to specify column mappings for simple primitive types. There are still a few bits of metadata that you can use to fine-tune your mappings. In this section, you’ll learn more annotations for more complex property mappings. Java Persistence has mappings for JDBC Blobs and Clobs, serializable objects, and embed-dable objects, as well as optimistic concurrency with version properties. We discuss all of these.

@Transient

In our first example of our Employee bean class, we showed that the persistence manager would assume that every nontransient property (getter/setter or field, depending on your access type) in your bean class is persistent, even if the property does not have any mapping metadata associated with it. This is great for fast prototyping of your persistent objects, especially when your persistence vendor supports autotable generation. However, you may have properties that you don’t want to be persistent, and therefore this default behavior is inappropriate. For instance, let’s assume we want to express what an employee is currently doing without tying this information to persistent storage. We may very simply declare:

tmp95233_thumb_thumb

When you annotate a property with @javax.persistence.Transient, the persistence manager ignores it and doesn’t treat it as persistent.

@Basic and FetchType

The @Basic annotation is the simplest form of mapping for a persistent property. This is the default mapping type for properties that are primitives, primitive wrapper types, java.lang.String, byte[], Byte[], char[], Character[], java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar, java.sql.Date, java. sql.Time, and java.sql.Timestamp. You do not need to tell your persistence manager explicitly that you’re mapping a basic property, because it can usually figure out how to map it to JDBC using the property’s type:

tmp95234_thumb_thumb

Usually, you would never annotate your properties with this annotation. However, at times you may need to specify the fetch() attribute, which allows you to specify whether a particular property is loaded lazily or eagerly when the persistent object is first fetched from the database. This attribute allows your persistence provider to optimize your access to the database by minimizing the amount of data you load with a query. So, if the fetch() attribute is LAZY, that particular property will not be initialized until you actually access this field. All other mapping annotations have this same attribute. The weird thing about the specification, though, is that the fetch() attribute is just a hint. Even if you mark the property as LAZY for a @Basic type, the persistence provider is still allowed to load the property eagerly. This is due to the fact that this feature requires class-level instrumentation. It should also be noted that lazy loading is neither really useful nor a significant performance optimization for standard, small objects, as loading these later requires the overhead of more SQL queries. It is best practice to eagerly load basic properties, and lazily load ones that may be large and infrequently accessed.

The optional() attribute is useful for when the persistence provider is generating the database schema for you. When this attribute is set to true, the property is treated as nullable.

Assuming employees may have a picture associated with their record, we probably don’t need this picture all the time. We may lazily load it and allow the property to be nullable:

tmp95235_thumb_thumb

Because we have a direct byte value here, we must also provide a mapping via the @Lob annotation.

@Lob

Sometimes your persistent properties require a lot of memory. One of your fields may represent an image or the text of a very large document. JDBC has special types for these very large objects. The java.sql.Blob type represents binary data, and java.sql.Clob represents character data. The @javax.persistence.Lob annotation is used to map these large object types. Java Persistence allows you to map some basic types to an @Lob and have the persistence manager handle them internally as either a Blob or a Clob, depending on the type of the property:

tmp95236_thumb_thumb

Properties annotated with @Lob are persisted in a:  

tmp95237_thumb_thumb

@Temporal

The @Temporal annotation provides additional information to the persistence provider about the mapping of a java.util.Date or java.util.Calendar property. This annotation allows you to map these object types to a date, a time, or a timestamp field in the database. By default, the persistence provider assumes that the temporal type is a timestamp:

tmp95238_thumb_thumb

For example, say we want to add the date of hire for an employee. We could add this support like so:

tmp95239_thumb_thumbtmp95240_thumb_thumb

The since property is stored in the database as a DATE SQL type.

@Enumerated

The @Enumerated annotation maps Java enum types to the database.

tmp95241_thumb_thumb

A Java enum property can be mapped either to the string representation or to the numeric ordinal number of the enum value. For example, let’s say we want an Employee entity property that designates the kind of employee: manager or peon. This could be represented in a Java enum like so:

tmp95242_thumb_thumb

You are not required to use the @Enumerated annotation to map a property. If you omit this annotation, the ORDINAL EnumType value is assumed.

Next post:

Previous post: