users@jpa-spec.java.net

[jpa-spec users] [jsr338-experts] Re: converters

From: michael keith <michael.keith_at_oracle.com>
Date: Wed, 08 Feb 2012 12:07:38 -0500

One thing that we might still consider adding is a comment about schema
generation.
The provider should use the database type from the converter for an
attribute that is slated for conversion.
If a @Column.columnDefinition is specified it should take precedence,
though.

-Mike

On 06/02/2012 5:30 PM, Linda DeMichiel wrote:
> Here's a more formal writeup of the converter proposal, which factors
> in results of the recent discussion.
>
> In conjunction with the consensus around extending the original
> proposal to "autoapply" converters, I've also extended the syntax
> to allow @Convert to be specified more broadly as well as to allow
> for disabling autoapply converters.
>
> Mike and I have already gone through the details here several times.
> Please review and post to the group if you think we should make any
> changes or if there are any corrections.
>
> I have left as an open issue whether we should also add a capability
> to add converters dynamically or whether we should defer this pending
> further feedback from developers.
>
> -Linda
>
> ---------------------------
>
> Converters may be specified to provide conversion between the entity
> attribute representation and the database representation for
> attributes of basic types. Converters may be used to convert
> attributes defined by entity classes, mapped superclasses, or
> embeddable classes.
>
> The conversion of all basic types are supported except for the
> following: Id attributes, version attributes, relationship attributes,
> and attributes explicitly annotated (or designated via XML) as
> Enumerated or Temporal. Auto-apply converters will not be applied to
> such attributes, and applications that apply converters to such
> attributes through use of the Convert annotation will not be portable.
>
> The persistence provider runtime is responsible for invoking the
> corresponding conversion method when loading the entity attribute from
> the database and before storing the entity attribute state to the
> database. Instances of attribute values used within JPQL or criteria
> queries (such as in comparisons, bulk updates, etc.) must be converted
> to their database types by the provider before being sent to the
> database for the query execution. If the result of a JPQL or criteria
> query includes one or more specific entity attributes that have been
> designated for conversion, then the results must be converted to their
> entity attribute representation before being returned.
>
> An attribute converter must implement the
> javax.persistence.mapping.AttributeConverter interface.
>
> /**
> * 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 AttributeConverter<X,Y> {
>
> /**
> * Converts the value stored in the entity attribute into the data
> * representation to be stored in the database.
> *
> * @param attribute the entity attribute value to be converted
> * @return the converted data to be stored in the database column
> */
> public Y convertToDatabaseColumn (X attribute);
>
> /**
> * Converts the data stored in the database column into the value
> * to be stored in the entity attribute.
> * Note that it is the responsibility of the converter writer to
> * specify the correct dbData type for the corresponding column for
> * use by the JDBC driver, i.e., persistence providers are not
> expected
> * to do such type conversion.
> *
> * @param dbData the data from the database column to be converted
> * @return the converted value to be stored in the entity attribute
> */
> public X convertToEntityAttribute (Y dbData);
> }
>
>
> A converter class must be annotated with the Converter annotation or
> defined in the object/relational mapping descriptor as a converter.
>
> /**
> * Specifies that the annotated class is a converter and defines its
> scope
> */
> @Target({Type})
> @Retention(RUNTIME)
> public @interface Converter {
>
> /**
> * If set to true, specifies that the converter will automatically
> * be applied to all mapped attributes of the specified
> * target type pair for the entities in the persistence unit,
> * unless overridden by means of the Convert annotation (or XML
> equivalent).
> * In determining whether a converter is applicable to an attribute,
> * the provider must treat primitive types and wrapper types as
> equivalent.
> *
> * Note that Id attributes, version attributes, relationship
> * attributes, and attributes explicitly annotated as Enumerated
> * or Temporal (or designated as such via XML) will not be converted.
> *
> * If autoApply is false, only those attributes of the target type
> pair
> * for which the Convert annotation (or corresponding XML element) has
> * been specified will be converted.
> *
> * The behavior is undefined if there are two converters defined for
> * the same target types and the Convert annotation is not used
> * to explicitly specify use of a converter.
> *
> * Note that if autoApply is true, the Convert annotation may be
> used to
> * override or disable auto-apply conversion on a per-attribute basis.
> */
> boolean autoApply() default false;
> }
>
>
> Type conversion may be specified at the level of individual attributes
> by means of the Convert annotation. The Convert annotation may also be
> used to override or disable an auto-apply conversion.
>
> The Convert annotation may be applied directly to an attribute of an
> entity, mapped superclass, or embeddable class to specify conversion of
> the attribute or to override the use of a converter that has been
> specified as autoApply=true. When persistent properties are used, the
> Convert
> annotation is applied to the getter method.
>
> The Convert annotation may be applied to an entity that extends a mapped
> superclass to specify or override the conversion mapping for an inherited
> basic or embedded attribute.
>
>
> @Target({METHOD, FIELD, TYPE})
> @Retention(RUNTIME)
> public @interface Convert {
>
> /**
> * Specifies the converter to be applied. A value for this
> * element must be specified if multiple converters would
> * otherwise apply.
> */
> Class converter() default void.class;
>
> /**
> * The attributeName must be specified unless the Convert annotation
> * is on an attribute of basic type or on an element collection of
> basic
> * type. In these cases, attributeName must not be specified.
> */
> String attributeName() default "";
>
> /**
> * Used to disable an auto-apply or inherited converter.
> * If disableConversion is true, the converter element should
> * not be specified.
> */
> boolean disableConversion() default false;
> }
>
> /**
> * Used to group Convert annotations
> */
> @Target({METHOD, FIELD, TYPE})
> @Retention(RUNTIME)
> public @interface Converts {
> Convert[] value();
> }
>
>
>
> The Convert annotation is used to specify the conversion of a Basic
> (whether explicit or default) field or property. The Convert annotation
> should not be used to specify conversion of the following:
> Id attributes, version attributes, relationship attributes, and
> attributes
> explicitly annotated (or designated via XML) as Enumerated or Temporal.
> Applications that specify such conversions will not be portable.
>
>
> The Convert annotation may be applied on a basic attribute or
> an element collection of basic type (in which case the converter
> is applied to the elements of the collection). In these cases, the
> attributeName element must not be specified.
>
> Examples:
>
> @Converter
> public class BooleanToIntegerConverter implements
> AttributeConverter<Boolean, Integer> { ... }
>
> @Converter(autoApply=true)
> public class EmployeeDateConverter implements
> AttributeConverter<com.acme.EmployeeDate, java.sql.Date> { ... }
>
> @Entity
> public class Employee {
> @Id long id;
>
> @Convert(BooleanToIntegerConverter.class)
> boolean fullTime;
> ...
> // EmployeeDateConverter is applied automatically
> EmployeeDate startDate;
> }
>
>
> // Apply a converter to an element collection of basic type
> @ElementCollection
> @Convert(NameConverter.class) // applies to each element in the
> collection
> List<String> names;
>
>
> // Apply a converter to an element collection that is a map of basic
> values
> // The converter is applied to the map *value*
> @ElementCollection
> @Convert(EmployeeNameConverter.class)
> Map<String, String> responsibilities;
>
> When the Convert annotation is applied to a map to specify conversion
> of a map key of basic type, "key" must be used to specify that it
> is the map key that is to be converted.
>
> // Apply a converter to a Map key of basic type (relationship)
> @OneToMany
> @Convert(converter=ResponsibilityCodeConverter.class,
> attributeName="key")
> Map<String, Employee> responsibilities;
>
> // Apply a converter to a Map key of basic type (element collection)
> @ElementCollection
> @Convert(converter=ResponsibilityCodeConverter.class,
> attributeName="key")
> Map<String, String> responsibilities;
>
>
> // Disable conversion in the presence of an autoApply converter
> @Convert(disableConversion=true)
> String myString;
>
>
> The Convert annotation may be applied on an embedded attribute or on a
> map collection whose key or value is of embeddable type (in which case
> the converter is applied to the specified attribute of the embeddables
> contained in the collection). In these cases the attributeName
> element must be specified.
>
> To override conversion mappings at multiple levels of embedding, a dot
> (".") notation form must be used in the attributeName element to
> indicate an attribute within an embedded attribute. The value of each
> identifier used with the dot notation is the name of the respective
> embedded field or property.
>
> When the Convert annotation is applied to a map containing
> embeddables, the attributeName element must be specified, and "key."
> or "value." must be used to prefix the name of the attribute that is
> to be converted in order to specify it as part of the map key or map
> value.
>
> // Apply a converter to an embeddable attribute
> @Embedded
> @Convert(converter=CountryConverter.class, attributeName="country")
> Address address;
>
>
> // Apply a converter to a nested embeddable attribute:
> @Embedded
> @Convert(converter=CityConverter.class, attributeName="region.city")
> Address address;
>
> @Entity public class PropertyRecord {
> ...
> // Apply a converter to a nested attribute of an embeddable that is
> // a map key of an element collection
> @Convert(name="key.region.city", converter=CityConverter.class)
> @ElementCollection
> Map<Address, PropertyInfo> parcels;
> }
>
> @OneToMany
> // Apply to an embeddable that is a map key for a relationship
> @Convert(attributeName="key.type",
> converter=ResponsibilityTypeConverter.class)
> Map<Responsibility, Employee> responsibilities;
>
>
> The Convert annotation may be applied to an entity class that extends
> a mapped superclass to specify or override a conversion mapping
> for an inherited basic or embedded attribute.
>
> // Override conversion mappings for attributes inherited from a mapped
> superclass
> @Entity
> @Converts({
> @Convert(attributeName="startDate", converter=DateConverter.class),
> @Convert(attributeName="endDate", converter=DateConverter.class)})
> public class FullTimeEmployee extends GenericEmployee { ... }
>
>
>
> Open Issue:
>
> Do we need an EntityManagerFactory method to dynamically add converters?
>
>
>
>
>
>
>