jsr338-experts@jpa-spec.java.net

[jsr338-experts] Re: converters

From: Emmanuel Bernard <emmanuel.bernard_at_jboss.com>
Date: Wed, 22 Feb 2012 10:15:28 +0000

I don't have a specific opinion. I think I like the current approach which is to keep the original field type at the query level and apply conversion underneath. That also works best for the type safe criteria API.

On 14 févr. 2012, at 17:05, Pinaki Poddar wrote:

> Yes, Emmanuel, as long as we restrict the generic type arguments of
> Convert<X,Y> to retain their cardinality, we will be maintaining
> "separation of concern" between mapping and conversion.
> What are your thoughts about relaxing the usage of "converted fields" in
> query expressions? Should we allow a URL field converted as String be used
> as String in queries?
>
>
> Regards --
>
> Pinaki
>
>
>
>
>
>
>
>
> From: Emmanuel Bernard <emmanuel.bernard_at_jboss.com>
> To: jsr338-experts_at_jpa-spec.java.net
> Date: 02/14/2012 12:16 AM
> Subject: [jsr338-experts] Re: converters
>
>
>
> 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.
>>
>
>
>