Great idea. I think this will help make SQLMaps even more usable. I do
have a few questions:
1- Does the class (interface) passed to the getMapper() method need to
extend a special iBatis interface (as with the Dao interface)?
2- Can this handle extended interfaces? For example, interface B extends
interface A. Are both interfaces "mapped" like you described? This can
be very interesting for common persistence methods for example. One
would simply need to extend a CustomPersistenceMapper interface instead
of redefining every method.
3- What about namespaces? Can they or are they being used?
4- Can we still use startBatch/executeBatch?
5- Can you explain how insert() and insertXXXX() differs (besides the
executed statement's name)?
Cheers,
Philippe
Clinton Begin wrote:
Although quite simple, there are some tradeoffs with the typical
SqlMapClient methods like:
Document doc = (Document) sqlMap.queryForList("getDocument", new
Integer (1));
First of all, it is possible that you could spell getDocuments
incorrectly. Second, the parameter is not strongly typed. So at code
time, you could easily pass in an inappropriate object. Also, the
return type is cast, so it's even possible for the statement to return
an invalid object (i.e. result map returns a Dog instead of a
Document). Finally, if you're using anything less than J2SE 5.0, you
have to wrap primitives with their wrapper types. DISCLAIMER: Yes, you
should have unit tests to verify this anyway! ;-)
But what else can we do about this? Well, what if we mapped the
"getDocument" mapped statement to an interface. For example, this one:
public interface DocumentMapper {
Document getDocument (int id);
}
So basically we have a method that mirrors the queryForList signature,
except the method name matches the mapped statement name, instead of
passing it as a parameter. Furthermore, as soon as the SqlMapClient is
built, this method is validated against the mapped statement to ensure
that the proper parameter and result types are defined. Finally, using
the sucker is a whole lot easier:
Document doc = documentMapper.getDocument(1);
No more casting. No more wrapping. No more ambiguous types. No more
misspelling.
Sounds good, how do I create a Mapper? Well, we already have. The
interface above is all we need. A simple call to the following
SqlMapClient method, creates the instance that can be used:
DocumentMapper documentMapper = (DocumentMapper)
sqlMap.getMapper(DocumentMapper.class);
The instance is thread safe, so you can keep this sucker around as a
field on your DAO or service class.
Best of all, unit testing becomes a snap, as you can mock a
DocumentMapper a heck of a lot easier than you could the SqlMapClient
interface.
Alrighty! So when will it be implemented? It already is. It's in SVN
right now for your perusal, here's the unit test:
http://svn.apache.org/repos/asf/incubator/ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/BindingTest.java
<http://svn.apache.org/repos/asf/incubator/ibatis/trunk/java/mapper/mapper2/test/com/ibatis/sqlmap/BindingTest.java>
The JavaDoc is below.
The current implementation isn't optimized, nor does it perform full
validation. In it's current state, it's mostly intended to be easily
removed if you don't like the idea.
So let us know what you think!
Cheers,
Clinton
----------------------------------------
getMapper
public java.lang.Object *getMapper*(java.lang.Class iface)
Returns a generated implementation of a cusom mapper class as
specified by the method parameter. The generated implementation will
run mapped statements by matching the method name to the statement
name. The mapped statement elements determine how the statement is
run as per the following:
* <insert> -- insert()
* <update> -- update()
* <delete> -- delete()
* <select> -- queryForObject, queryForList or queryForMap, as
determined by signature (see below)
* <procedure> -- determined by method name (see below)
How select statements are run is determined by the method signature,
as per the following:
* Object methodName (Object param) -- queryForObject
* List methodName (Object param [, int skip, int max | , int
pageSize]) -- queryForList
* Map methodName (Object param, String keyProp [,valueProp]) --
queryForMap
How stored procedures are run is determined by the method name, as
per the following:
* insertXxxxx -- insert()
* createXxxxx -- insert()
* updateXxxxx -- update()
* saveXxxxx -- update()
* deleteXxxxx -- delete()
* removeXxxxx -- delete()
* selectXxxxx -- queryForXxxxxx() determined by method signature
as above
* queryXxxxx -- queryForXxxxxx() determined by method signature
as above
* fetchXxxxx -- queryForXxxxxx() determined by method signature
as above
* getXxxxx -- queryForXxxxxx() determined by method signature as
above
*Parameters:*
|iface| - The interface that contains methods representing the
mapped statements contained.
*Returns:*
An instance of iface that can be used to call mapped statements
directly in a typesafe manner.