Technically it seems sound. It's certainly not as pure as a real proxy based on the interface, as there's no way we can assert that the resultmap matches the interface. So you'd have to be sure that you unit test appropriately.
The only technical caveat is that you would be REQUIRED to use CGLIB. There would be no option, becasue you'd always be creating dynamic proxies for concrete classes.
The other thing is a question of simple design practices. If an interface contract only guarantees a return value of type A, then you're really making an assumption when you cast it to A1. A better design would really be to either change the interface to include all functionality that you need, or change the return type. That's being more honest with yourself. Ask: "what is this interface really hiding if I'm just going to cast out of it anyway?"
IMHO using the result map is less "honest", because you're really "guessing" at types by casting them.
Cheers,
Clinton
On 8/11/05, Oleg Shpak <[EMAIL PROTECTED]> wrote:
Hello,
I'm having a problem with iBatis which I'm keen to solve by patching
iBatis (other solutions exist, but they are a bit cumbersome).
In short, I propose to modify loadResult() method in
com.ibatis.sqlmap.engine.mapping.result.loader.EnhancedLazyResultLoader ,
so that is creates more "honest" proxies, proxies which base on lazy
loading statement's resultMap result class, rather than just on method
return type.
---------------------------------------------------------------------------------------------
public Object loadResult() throws SQLException {
if (DomTypeMarker.class.isAssignableFrom(targetType)) {
return ResultLoader.getResult(client, statementName,
parameterObject, targetType);
} else if (Collection.class.isAssignableFrom(targetType)) {
return Enhancer.create(Object.class, INTERFACES, this);
} else if (targetType.isArray() ||
ClassInfo.isKnownType(targetType)) {
return ResultLoader.getResult(client, statementName,
parameterObject, targetType);
} else {
// NEW CODE STARTS
Class type;
try {
ResultMap resultMap =
client.getDelegate()
.getMappedStatement(statementName).getResultMap();
type = resultMap.getResultClass();
} catch (SqlMapException e) {
type = targetType;
}
return Enhancer.create(type, this);
// NEW CODE ENDS
// old code was
// return Enhancer.create(targetType, this);
}
}
---------------------------------------------------------------------------------------------
The problem
When a proxy is created for an object, target type is determined using
the property method return type. It is possible that the actual return
value of a method is a subclass of the return type (especially if the
return type is an interface).
// public interfaces
interface A {...}
interface B {
A getA();
...
}
// implementation classes
class A1 implements A { ...}
class B1 implements B {
private A1 a;
public A getA(){
return a; //an instance of A1
}
...
}
// result maps
<resultMap id="A" class="A1">
...
</resultMap>
<resultMap id="B" class="B1">
<result property="a" column="a_id" select="getA"/>
...
</resultMap>
<select id="getA" resultMap="A">
...
</select>
Here I configured lazy loading for B1.getA. The proxy which is being
generated is based on type A, not on type A1. My DAO class for type A
actually returns instances of type A1. Generally it is OK for it (DAO
class) to consume objects of type A, but in some cases, when it needs to
modify some implementation details it has to cast them down to A1. This
is where proxies cause a ClassCastException. "Honest" proxies don't.
What do you think?
Probably I put my code not into the right place, or it, probably, should
be optionally turned on/off.
Regards,
Oleg
