users@jpa-spec.java.net

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

From: Linda DeMichiel <linda.demichiel_at_oracle.com>
Date: Mon, 19 Dec 2011 13:22:28 -0800

Hi all,

It would be good to get some feedback from you on this proposal!

As I hope you might expect, I don't have any major feedback since
Mike and I have discussed this beforehand :-) when he was kind enough
to volunteer to drive this feature.

The few suggestions I do have are embedded below. Otherwise, I
think we should proceed to add this feature to the spec.

thanks,

-Linda


On 12/9/2011 1:02 PM, michael keith wrote:
> 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.)
>

I would keep this in javax.persistence, as it is pretty basic functionality.
We can revisit later on (i.e. until JPA 2.1 PFD) if we change our mind.

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

I would drop "Entity" from the interface name

> /**
> * 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)
> }
> }
>

Since the converter is a class (unlike TableGenerator and SequenceGenerator),
I'm thinking it would be nicer if we could apply the annotation to the
converter class rather than to an entity, mapped superclass, or attribute
in order to define the class as a converter.

We could then default the name of the converter to the lowercased
unqualified class name, or allow a name to be specified.

e.g.,

@Converter //name defaults to booleanToInteger
public class BooleanToInteger implements AttributeConverter {...}

or
@Converter(name="booleanToInt")
public class BooleanToInteger implements AttributeConverter {...}



> |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.)
>

Yes

> 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.
>

If we follow the suggestion above, I would do this at mapping file or persistence unit levels only.

> 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
>