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

From: michael keith <>
Date: Mon, 16 Jan 2012 16:33:57 -0500

On 16/01/2012 3:33 AM, Oliver Gierke wrote:
> Hi all,
> trying to reply Bernd's and Mike's replies.
>>>> 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.
>>> Why shouldn't it be allowed for embeddables?
>> There is nothing stopping someone from using it on mappings *within* an embeddable, but an embeddable as a whole entails a number of mapped fields and we are not supporting multiple columns in this round.
> Point taken. Do the types of the properties to be converted need to be known as JPA-special types (@Entity, @MappedSuperclass) at all then? The availability of a converter of property type X to natively mappable "column" type Y pretty much hands responsibility of mapping the object, right?
Not sure what you mean. A converter converts only a single attribute in
an entity, mapped superclass, or embeddable, not the entity/mapped
superclass/embeddable itself.
>>>> 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.
>>> Does this mean the converters would be instantiated by the persistence provider then? This would expose it to the same drawbacks EntityListeners currently face: injecting objects into them is quite complicated.
>> Yes, they would be similar to EntityListeners in the fact that they would be instantiated by the provider. Injection is not as useful, though, since the point of converters is simply to transform attribute state from one form to another, not to perform domain operations. That doesn't mean we can't allow it, it's just not a priority IMO.
> I didn't think of injecting an EntityManager or other sort of application component but rather of a way to configure a converter implementation by some means, e.g. configuring a date pattern a Date object shall be read and written into. I don't think it's a good idea to defer this question to a later stage as it requires quite some changes to the API and implementations if introduced later.
Is a date pattern something that is unique to the converter and that
could be defined in some static state within it?
I guess it's possible that you might need some externally defined state
in order to do the conversion, it just feels like making converters
something like CDI beans might be overkill.
> I also think that the instance creation of user defined extensions (what the EntityAttributeConverter actually is) should not be hardcoded into the persistence provider.
Converter implementation classes would not be hardcoded into the
provider any more than entity classes or any other user class would. The
provider discovers the converter class just like it does entity classes,
listeners, or other user-defined classes, and instantiates them
reflectively on demand. No hardcoding necessary.
> Other comments on Mike's suggestions regarding converter annotations and EntityManagerFactory API see below.
> Am 16.01.2012 um 06:05 schrieb Bernd Müller:
>> I see a problem: the name of the converter is unique in the pu but
>> the usage mimics locality for an entity.
>> In JSF there is also a converter annotation called FacesConverter
>> doing similar things. However it is used to annotate the converter (as
>> the name implies ?)
>> The refactored example:
>> @Converter("booleanToInteger")
>> public class BooleanToIntegerConverter implements
>> ...
>> }
>> @Entity
>> public class Employee {
>> @Id long id;
>> @Convert("booleanToInteger")
>> boolean fullTime;
>> ...
>> }
> +1. Although there's one caveat to this. As the flip switch to define whether a converter is a global one or not is now tied to the implementation (as it resides in the annotation) the implementation cannot be reused for the exact opposite use case. Besides this flag the @Converter
I think it's reasonable to state declaratively in the converter whether
it is a global conversion or not. That is probably where you would want
to do it. If you don't want to put it there, though, you could always
put it in an XML entry instead to decouple it from the code entirely.
> annotation is just what @Named is. So I wonder if it might make sense to simply consider managed beans of a given type (EntityAttributeConverter<X, Y>) as converters? But that might be an implementation detail of the EntityAttributeConverterFactory (see below).
Hmmm, a factory seems like an unnecessary addition to the model.
> So generally I think a "global-by-default" approach would remove the need to have this special flag at the implementation side of things. We could then have the @Convert annotation look something like this:
This is just removing the autoApply option at the expense of the
non-default case (which becomes uglier).
> @Target({METHOD, FIELD})
> @Retention(RUNTIME)
> public @interface Convert {
> String value() default "";
> boolean disabled() default false; //
> }
> This would result in an opt-out model like this:
> 1. if a property is not annotated we look for an EntityAttributeConverter<X, Y> with X as the property type or less concrete
> 2. if a property is annotated use the converter defined in value() or skip conversion if disabled() is set to true
> The @Converter annotation is entirely obsolete then and the decision which converter to be used is not split between two annotations. The according EntityManagerFactory methods could look like this:
> public interface EntityManagerFactory {
> void setEntityAttributeConverterFactory(EntityAttributeConverterFactory factory);
> void addEntityAttributeConverter(Class<? extends EntityAttributeConverter<?, ?>> converterType);
> }
> The EntityAttributeConverterFactory methods could then look like {
> public interface EntityAttributeConverterFactory {
> <X, Y> EntityAttributeConverter<X, Y> getConverter(Class<? extends EntityAttributeConverter<X, Y> converterType);
> }
>> The Converter annotation uses "value" instead of "name" and is therefore
>> a little bit less verbose.
> +1
>> And the usage of "booleanToInteger" reflects its uniqueness instead
>> of doubling the string literal in one single entity class.
>> To demand for global application of the converter there is a second
>> attribute "forClass" in @Converter defining the Class to apply for.
>> Example
>> @Converter(forClass=SomeBusinessPropertyType.class)
>> public class ConverterForSomeBusinessPropertyType {
>> ...
>> }
> I don't think this is necessary here as we can use a generic interface the type parameters can be extracted from. I think using the interface based approach trumps an annotation based one here as the metadata reflected in the annotation attributes can be used in the method signatures to make the SPI a bit neater to implement.
> Cheers,
> Ollie