users@jpa-spec.java.net

[jpa-spec users] [jsr338-experts] mapping conversion

From: michael keith <michael.keith_at_oracle.com>
Date: Fri, 09 Dec 2011 16:02:24 -0500

Hi everyone,

One of the most common feature requests from people who are mapping
entities using JPA is a way to transform or convert the state from the
way it is stored in the entity attribute to the way it is stored in the
database, and vice versa. A “converter” provides this functionality.
Below is a proposal for converters.

A converter is an object that implements the
javax.persistence.mappings.EntityAttributeConverter interface, defined as:

(*** OPEN ISSUE: Should the package just be javax.persistence or should
a subpackage be used in anticipation of other mapping extensions? Some
people have asked for a mapping API and this class would probably fit in
with that.)

(*** OPEN ISSUE: Is there a better classname and method pair?)

/**
* A class that implements this interface can be used to convert entity
attribute state
* into database column representation and back again. Note that the X
and Y types may
  * be the same Java type.
  *
  * @param X The type of the entity attribute
  * @param Y The type of the database column
*/
|public interface EntityAttributeConverter<X,Y> {||
     /** ||
      * Converts the object stored in the entity attribute into the data
representation||
|| * to be stored in the database.
      *
      * @param attributeObject The object in the entity attribute to be
||converted
      * @return The converted data to be stored in the database column||
      */||
     public Y convertToDatabaseColumn (X attributeObject);||
||
     /** ||
      * Converts the data stored in the database column into an object ||
      * to be stored in the entity attribute.||
      *||
      * @param dbData The data from the database column to be converted||
      * @return The converted state to be stored in the entity attribute||
      */||
     public X convertToEntityAttribute (Y dbData);||
}

|A converter is defined using a javax.persistence.Converter annotation.
It is similar to a TableGenerator and SequenceGenerator in that it can
be defined on any entity class, mapped superclass, or entity attribute,
and given a name, but the name must be unique within the scope of the
persistence unit.

The annotation to use on the attribute is defined as:

|_at_Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface Converter {
String name();
Class converterClass();
}
|
In order to make use of a converter the @Convert annotation is used on
an entity or mapped superclass attribute. The @Convert annotation is
defined as:

|_at_Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Convert {
String value();
}
|
It annotates an entity attribute and specifies the name of the
converter. The same converter can be referenced by any number of
@Convert annotated attributes.
Example:

|_at_Entity
@Converter(name="booleanToInteger",
            converterClass=BooleanToIntegerConverter.class)
public class Employee {
@Id long id;
@Convert("booleanToInteger")
boolean fullTime;
...
}||
|
|public class BooleanToIntegerConverter implements
EntityAttributeConverter<Boolean,Integer> {
public Integer convertToDatabaseColumn (Boolean attributeObject) {
return (attributeObject ? 1 : 0);
}
public Boolean convertToEntityAttribute (Integer dbData) {
return (dbData > 0)
}
}

|The Container is responsible for invoking the corresponding method when
loading a converted entity attribute from the database, or before saving
that entity attribute state to the database.

The @Convert annotation may be used on default or explicitly mapped
basic attributes. It is not portably supported to use @Convert on
mappings marked with @Enumerated, @Temporal, @Id, @Version or on
embedded, relationship or element collection attributes.

(*** OPEN ISSUE: Should we require providers to support some of these
mappings? I can see supporting the conversion of temporal and enumerated
attribute types, but in those cases @Convert would be used on its own,
not in combination with either @Enumerated or @Temporal.)

Schema Generation

When using schema generation a provider may choose to assume the column
type matches the entity attribute type if no additional
@Column.columnDefinition is supplied, or a provider may have an
alternate way of deciding the column type for an entity attribute.

Queries

Instances of attribute values in JP QL or criteria queries (such as in
comparisons or bulk assignments, etc) must be converted to database
types before the queries are executed. If the result of a query includes
one or more specific entity attributes then the results must be
converted to entity attribute form before being returned.

XML

Converters can be defined in XML at method, class, mapping file or
persistence unit level, similar to generators, and will override the
same named converters if such converters were defined in annotation form.

Enhancements

This proposal covers converting data from a single column to a single
entity attribute. The assumptions are:

a)This will cover the 90% case

b)More advanced cases, such as multiple columns to a single attribute,
can be covered by a more advanced mapping or a more advanced converter
type that can be added later if necessary

c)It is not worth convoluting the simple case to handle a much less
likely complex case

Comments?

-Mike