users@jpa-spec.java.net

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

From: Emmanuel Bernard <emmanuel.bernard_at_jboss.com>
Date: Tue, 14 Feb 2012 09:15:53 +0100

Just for the record, I have not really asked for a way to change cardinality. I just wanted to be able to use converters on Collection<X>.

On 13 févr. 2012, at 21:01, Pinaki Poddar wrote:

> 1. Using conversion to change mapping cardinality such as a
> @ElementCollection List<String> to a encoded single String is possible, but
> such facility/technique seems to encroach upon responsibilities of mapping
> primitives dictating the database schema. It may also impact how
> effectively queries (e.g. IN operator involving the original List<String>
> attribute) can be executed.
>
> 2. how the converted attributes are used in query context (in expressions,
> as parameter values) can be clarified further. The strict rule that they
> are only be used as their original declared type will be simpler at the
> cost of prohibiting some useful facility. For example, if a field declared
> as java.net.URL is converted to a java.lang.String, then a query that uses
> LIKE operator or orders the result on the (stringfied) URLs can be useful
> at the expense of deviating from such a strict rule.
>
>
> Regards --
>
> Pinaki Poddar
>
>
>
>
>
>
>
>
> From: Gordon Yorke <gordon.yorke_at_oracle.com>
> To: jsr338-experts_at_jpa-spec.java.net,
> jsr338-experts_at_jpa-spec.java.net
> Date: 02/10/2012 11:58 AM
> Subject: [jsr338-experts] Re: converters
>
>
>
> 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.
>