We suggest a change in how OpenJPA determines the names to be used for
DB columns when using value handlers that use multiple columns. When a
@Column annotation (or the equivalent in XML) is found for a property
or field that is handled by a value handler, the name passed to the
map() method should be the name in the annotation, and not the name of
the property or field.
While working with ValueHandlers that use multiple columns, we found
the following behavior (using OpenJPA on WebSphere 6.1..., thus
OpenJPA 1.0.3. (?)):
For a developer of an entity that uses the complex value type for
which we write the value handler, the complex value type is "one
thing". She doesn't (want to) know the internal complexity. The goal
of complex value types is indeed encapsulation. The developer of the
entity also does not (want to) know on how many and which columns we
want to persist.
The name given as parameter in the map() function of a ValueHandler is
the name of the underlying property / field.
It is the intention to define names on the Columns we return that are
distinct for the different columns, in the context of any use (i.e.,
for many different properties that use our complex value type, in many
different entities, by many different developers). Therefor it is not
a bad idea to name the columns something like <code>name +
"_subcolumndescription"</code>.
E.g., we have a TimeInterval complex value type, that features a begin
and end property. We want, in particular circumstance, to persist time
intervals in 2 TIMESTAMP columns, called <code>name + "_begin"</code>
and <code>name + "_end"</code>. When applied to a property / field
called "subscriptionPeriod", you thus get in the database the columns
"SUBSCRIPTIONPERIOD_BEGIN" and "SUBSCRIPTIONPERIOD_END".
So far, so good.
Now, for several good reasons, developers might want to override the
(base) name used for column names. We don't want to use the name of
the field / property, but something else (particularly handy when your
style uses a prefix for instance variables like "$" or "_"). This is
supported in JPA with the @Column() annotation.
Now, we observe that when we combine this annotation with the OpenJPA
value handlers, that we get an error, because we have only defined 1
column name, and we need n for our property. The solution is to use
the OpenJPA @Columns({}) annotation, where we can nest a @Column()
annotation with a specific name for each column.
In our example, suppose we want to use "SUBSRC" as column name:
@Column("SUBSCR") gives an error
and
@Columns({@Column("SUBSCR_BEGIN"), @Column("SUBSCR_END")}) gives
indeed 2 columns with those names in the database. The naming is then
dependent on the order.
We suggest that this breaks encapsulation. To make this work, the
developer indeed needs to be aware of the internal of
TimeIntervalValueHandler.
We suggest a small change in OpenJPA: when calling the map() method,
give as name the name of the property / field if no @Column()
annotation is found, or if the @Columns() annotation is found
(although then it is irrelevant, but passing null is not a backward
compatible option). This is the current behavior. In the case the
@Column() annotation (or the equivalent in XML) is found however, pass
in the name given in the annotation as name parameter to the map()
method, instead of the current property / field name. Also, the
verification would have to be changed to not throw an error in this
case.
The behavior with @Columns() should be kept as fallback for legacy
databases (although in that case you probably will not be working with
complex value types and value handlers?).
|