users@jpa-spec.java.net

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

From: Oliver Gierke <ogierke_at_vmware.com>
Date: Mon, 16 Jan 2012 00:33:07 -0800

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?

>>> 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. I also think that the instance creation of user defined extensions (what the EntityAttributeConverter actually is) should not be hardcoded into the persistence provider.

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 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).

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:

@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

-- 
/**
 * @author Oliver Gierke - Senior Member Technical Staff
 *
 * @param email ogierke_at_vmware.com
 * @param phone +49-351-30929001
 * @param fax   +49-351-418898439
 * @param skype einsdreizehn
 * @see http://www.olivergierke.de
 */