In building a better bean processor I discussed some ideas for reflection mapping of a ResultSet to Object.
This time around we can look at a solution design.
Diagram
Here is a UML diagram of the solution (click on the image for a bigger, clearer picture).
Note that the diagram contains a couple of example extensions for the DBUtils BeanProcessor and Spring RowCallbackHandler. One of the requirements for the ResultSetMapper is that it can be easily be plugged into these bigger frameworks.
Basic Mapping
Data mapping will occur against ResultSet columns and Object fields.
By default the Object field will require the @MapToData annotation, though this requirement may be turned on/off with ResultSetMapper.setAnnotationRequired(true/false). Why have it off? There would be no requirement to add an empty ‘@MapToData’ annotation. So, why have it on? The source code contains a documentary ‘@MapToData’ annotation to indicate that some magic is being performed on the field.
The mapping will use a NameMatcher to compare the field name with the column name (unless a MapToData.columnAlias is specified). A default NameMatcher – NameConverter – will use simple camelCase to under_score matching.
Data mapping will occur, in order of priority:
- Via a specified setter procedure – MapToData.setter
- Via the JavaBean standard default setter
- Directly into the object field
Inheritance
Inheritance requires that many target classes may be supplied to the ResultSetMapper. However, this means that there must be some way of choosing which target to create. To allow selection of target objects, an ObjectValidator will be used – every type of target class will be constructed from the ResultSet and passed to the ObjectValidator. (The default validator always returns true.)
The advantage of this approach is that the programmer only deals in their domain object, but it does require an overhead of creating extra objects for validation.
Aggregates
Solving this problem requires a couple of technical problem answers:
- How to specify that an Object is an aggregate target (eg MyDataObject), rather than ‘Just Another Object’ (eg String, MyTransientObject)? Answer: MapToData.isAggregateTarget = true/false
- How to have multiple fields of the same class with different business meanings (eg MyDataObject start; MyDataObject end;)? Answer: MapToData.columnPrefix or MapToData.columnSuffix
Example
So the example domain from the previous post would now be:
public abstract class Jelly {
@MapToData
Long jellyId;
@MapToData
String jellyType;
}
public class JellyAttribute {
@MapToData
String name;
@MapToData
String value;
}
public class JellyCompany {
@MapToData
String companyName;
@MapToData
String address;
}
public class JellyBean extends Jelly {
@MapToData
String targetMarket;
@MapToData
BigDecimal weight;
@MapToData (columnPrefix="flavour_", isAggregateTarget = true)
JellyAttribute flavour;
@MapToData (columnPrefix="colour_", isAggregateTarget = true)
JellyAttribute colour;
@MapToData (isAggregateTarget = true)
JellyCompany company;
}
public class JellyCup extends Jelly {
@MapToData
String productName;
@MapToData ( columnAliases = { "cup_volume" } )
BigDecimal volume;
@MapToData (isAggregateTarget = true)
JellyAttribute shape;
}
public class JellyCupAndSpoon extends JellyCup {
@MapToData
String spoonMaterial;
@MapToData
BigDecimal spoonLength;
}
by
Pingback: A Few More Words » ResultSetMapper Project - Map a Java ResultSet to an Object
Is the source code available on sourceforge updated?. Because I recompile and it behaves diferently as expected. It difers from the JAR available.
Tnks
The source code is up-to-date. Any details the differences?