At the moment I’m coding up a ‘BeanMapper’. Something to automatically transform a ResultSet into a JavaBean object. (Why would you want to do this? Write and maintain less code will probably do as a worthwhile cause.)
The DBUtils Apache commons project has a BeanProcessor that does a basic job. It is good for a simple mapping, however it does not meet the following challenges:
- Inheritance – a ResultSet may map to different Objects of the same parent over different rows
- Aggregates – a ResultSet may contain the data to build some/all of the aggregate objects
- Multiple Objects – a ResultSet may contain the data for many objects in one row
Challenge #1 can occur whenever inheritance is used, Challenges #2 and #3 are linked with improving performance (though I’m finding it difficult to imagine a scenario for #3 right now).
As well as the challenges there are some improvements that may be made:
- The current BeanProcessor mapping works with the JavaBean standard – getColumnName/setColumnName will map to columnname in the database. A solution that also works with Annotations and/or Object fields would be helpful.
- Automatic name mapping of the object ‘standard’ (camelCase) to the database ‘standard’ (under_score) would also be helpful.
- Since I’m a user of the Spring framework, something that I can use as a default RowCallbackHandler
So what would my ideal mapping solution look like in operation?
I would have JDBC DAO that maps a variable ResultSet automatically to a set of target classes…
public interface JellyDAO {
public Jelly find(Long jellyId);
public List<Jelly> find(FindRequest findRequest);
}
public class JellyDAOJdbc implements JellyDAO {
List<Class> targetClasses;
List<Class> aggregateClasses;
BeanMapper beanMapper;
public jellyDAOJdbc() {
// The ResultSet may contain data for
// any of the following target classes.
targetClasses = new ArrayList<Class>();
targetClasses.add(JellyBean.class);
targetClasses.add(JellyCup.class);
targetClasses.add(JellyCupAndSpoon.class);
// The Target classes may contain the following classes
// that may also come across in the ResultSet.
aggregateClasses = new ArrayList<Class>();
aggregateClasses.add(JellyAttribute.class);
aggregateClasses.add(JellyCompany.class);
//
beanMapper = new BeanMapper(targetClasses, aggregateClasses);
}
private Jelly processResultSetRow(ResultSet resultSet) {
// Map the ResultSet to a Jelly class.
Jelly jelly = (Jelly) beanMapper.toBean(resultSet);
return jelly;
}
private List<Jelly> finder() {
// Obtain a ResultSet..
// Loop through result set..
{
jellyList.add(processResultSetRow(resultSet));
}
return jellyList;
}
}
The Jelly domain model might be something like…
public abstract class Jelly {
Long jellyId;
String jellyType;
}
public class JellyAttribute {
String name;
String value;
}
public class JellyCompany {
String companyName;
String address;
}
public class JellyBean extends Jelly {
String targetMarket;
BigDecimal weight;
JellyAttribute flavour;
JellyAttribute colour;
JellyCompany company;
}
public class JellyCup extends Jelly {
String productName;
BigDecimal volume;
JellyAttribute shape;
}
public class JellyCupAndSpoon extends JellyCup {
String spoonMaterial;
BigDecimal spoonLength;
}
public class JellyRepository {
public Jelly find(Long jellyId);
public List<Jelly> find(FindRequest findRequest);
}
I would also have a database view or stored procedure with the following signature…
jelly_id, jelly_type, target_market, product_name,
spoon_material, spoon_length, cup_volume, flavour_name,
flavour_value, colour_name, colour_value, company_name,
company_address, shape_name, shape_value
by
Pingback: A Few More Words » JDBC ResultSet Mapper
Ive been working on a framework built on DbUtils, which sole intention is to simplify database work to a single object that can be used simply in many threads. The one feature I really would like to add is the “aggregate” concept you referred to in your bean mapper. Do you have a working version of this, and could I use it in my project (of course you will be given full credit). The project is being released under the apache 2 lic.
Thanks in advance for your consideration,
@Karim: Have a look at resultsetmapper. It is the open source implementation of the discussed problem.
It can map resultset data to java beans (and their ‘aggregate’ components) in 2 ways:
Please let me know if it is of use, or if you would like me to improve in some way.
I have been working on JDBC using ResultSetMapper class. Here i am using Self join query. So mapping is not working if i bind duplicate columns to fields. Can you tell me solution.
Thanks in advance.
ResultSetMapper resultSetMapper = new ReflectionResultSetMapper(
EndUserBean.class);
return resultSetMapper.mapRow(userResultSet);
@venu bothsa Give the duplicate columns alias names in your SQL statement.