jsr338-experts@jpa-spec.java.net

[jsr338-experts] Re: converters

From: Gordon Yorke <gordon.yorke_at_oracle.com>
Date: Fri, 10 Feb 2012 15:57:28 -0400

Hello all,
  Just a couple comments:

>So that excludes the possibility to convert a collection object into a flat representation in String. do we want to cover this use case? I'm tempted to say that's a fair use case.
This case is covered through the @Basic annotation. Marking the collection attribute as @Basic and @Convert would allow the collection to be converted to a string.

> - we should support the listing of converters in persistence.xml (just like classes) in case people chose to disable scanning.
Yes.

Emmanuel Bernard <emmanuel.bernard_at_jboss.com> wrote:

>First off thanks for the very nice work by Mike and Linda, many of my
>initial concerns have been addressed in the formal writeup.
>
>We might want to be explicit in the spec that it's a first step to
>address 80% of the use cases and not a complete replacement of what
>specific vendors propose in this area. I fear some backslash otherwise
>esp for fairly common uses cases like:
>- one java type to multiple columns
>- use of database specific types / operation or JDBC specific code upon
>read/write of a type.
>
>More in line.
>
>Emmanuel
>
>On 6 févr. 2012, at 23:30, 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.
>
>Why not allowing @Id and @Version attributes provided the converted
>type is within the acceptable list of types for id and version?
>
>
>> 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.
>
>Have you considered `implicit` instead of `auto-apply`. I feel it
>flows nicely with the word converter.
>
>>
>> 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.
>
>+1 on removing Entity from the 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 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);
>
>An alternative and more neutral name could be:
>- convertForDatabase
>- convertFromDatabase
>
>or read / write.
>
>>
>> /**
>> * 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,
>
>I agree with Gordon, I was expecting X to be the sole element taken
>into account.
>What about attributes that have a subtype of X? If we allow subtypes,
>which rules do we have for "specialization"?
>Do we reuse the overloading rules of the JVM? What about potential
>conflicts, is that an exception at deployment time?
>
>> * 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.
>
>I'd rather have an exception here. Any reason to leave it undefined?
>
>> *
>> * 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 plural is unfortunate here :(
>In Bean validation we use an inner annotation @Convert.List but that's
>not in the spirit of this spec.
>
>>
>>
>> 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.
>
>So that excludes the possibility to convert a collection object into a
>flat representation in String. do we want to cover this use case?
>I'm tempted to say that's a fair use case.
>
>A way around that would be to make use of the attributeName to decide
>whether or not the collection itself or its elements are targeted. That
>has the benefit of rendering @Convert(attributeName="key") symmetric
>with @Convert(attributeName="value") (example below).
>
>>
>> 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?
>
>Does it mean I can also define an attribute as @Convert? What happens
>to auto-apply converters, would that reconsider existing entities?
>
>Besides all these comments, I have a few additional proposals for the
>group:
>- do we want converters to accept parameters to help people make them
>more generic. Using the boolean to string example, one could feed the
>converter with the string values it expect as true and false (y/N, 0/1
>etc).
>- we should support the listing of converters in persistence.xml (just
>like classes) in case people chose to disable scanning.