项目使用的持久层框架是ibatis3,即mybatis,开源包是ibatis-core-3.0.jar和ibatis-sqlmap-3.0-beta-10.jar(当然使用的是其中一个,其中的加载顺序在此不加赘述,原因是两个包相似度很高)。
在做项目的时候,需要返回HashMap数据类型,将每一行的id作为key,其他数据封装成某个数据类型最为value,在这里需要使用ResultHandler,不过该包不支持Proxy动态代理调用方式,只能调用DefaultSqlSession中的如下方法select(String statement, Object parameter, ResultHandler handler)才能使用。这不利于项目代码的统一,使用起来也不是很方便。代码如下:
//出自mybatis-3.0.1.jar, org.apache.ibatis.binding.MapperMethod.java
public Object execute(Object[] args) throws SQLException {
Object result;
if (SqlCommandType.INSERT == type) {
Object param = getParam(args);
result = sqlSession.insert(commandName, param);
} else if (SqlCommandType.UPDATE == type) {
Object param = getParam(args);
result = sqlSession.update(commandName, param);
} else if (SqlCommandType.DELETE == type) {
Object param = getParam(args);
result = sqlSession.delete(commandName, param);
} else if (SqlCommandType.SELECT == type) {
//在这里没有判断是否使用ResultHandler接口作为参数的情况
//而是判断返回类型是List,否则返回单行数据
if (returnsList) {
result = executeForList(args);
} else {
Object param = getParam(args);
result = sqlSession.selectOne(commandName, param);
}
} else {
throw new BindingException("Unkown execution method for: " + commandName);
}
return result;
}
//出自org.apache.ibatis.session.defaults.DefaultSqlSession.java
//该调用方法可使用ResultHandler接口
public void select(String statement, Object parameter, ResultHandler handler) {
select(statement, parameter, RowBounds.DEFAULT, handler);
}
在mybatis3.1.0之后,就支持了通过动态代理使用ResultHandler作为参数调用的方法,代码如下:
//出自mybatis-3.1.0.jar, org.apache.ibatis.binding.MapperMethod.java
public Object execute(Object[] args) {
Object result = null;
if (SqlCommandType.INSERT == type) {
Object param = getParam(args);
result = sqlSession.insert(commandName, param);
} else if (SqlCommandType.UPDATE == type) {
Object param = getParam(args);
result = sqlSession.update(commandName, param);
} else if (SqlCommandType.DELETE == type) {
Object param = getParam(args);
result = sqlSession.delete(commandName, param);
} else if (SqlCommandType.SELECT == type) {
//返回类型必须是void,并使用ResultHandler接口作为参数
if (returnsVoid && resultHandlerIndex != null) {
executeWithResultHandler(args);
} else if (returnsMany) {
result = executeForMany(args);
} else if (returnsMap) {
result = executeForMap(args);
} else {
Object param = getParam(args);
result = sqlSession.selectOne(commandName, param);
}
} else {
throw new BindingException("Unknown execution method for: " + commandName);
}
return result;
}
对比两个包中的代码,有两种优化方式可以选择:
1.修改mybatis3.0源代码,模仿mybatis3.1;
2.更换包为mybatis3.1.0
当然第一种方式修改源代码较为复杂,因此我选择第二种方法。
更换包后,重新部署环境,遇到以下问题:
1.原来包中Environmet不是final类型,新包变为final类型,在我的项目中有个地方继承了Environment,所以这个地方需要找个变通的方法修改,在此不加赘述
2.发布后,执行分页查询,后台报错,经查,是新旧包中某些类略有修改,而其他与之关联的包(例如rapidframework-3.9.jar)没有更新所致。例如:
在rapidframework-3.9.jar中有如下代码,并给出我的修改:
//出自rapidframework-3.9.jar:cn.org.rapid_framework.ibatis3.plugin.OffsetLimitInterceptor.java
private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {
Builder builder = new MappedStatement.Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
/* 下面这句ms.getKeyProperty()报错,原因是新包修改了MappedStatement类中keyProperty的数据类型,
`* 将String改为String[],相应的get方法由getKeyProperty()改为getKeyProperties()
* 因此,这里我是如下修改的,原因是在分页查询时,我跟踪此处的keyProperties为null,而其他查询并未使用此处
* String[] s = ms.getKeyProperties();
* if(s == null){
* builder.keyProperty(null);
* }else{
* builder.keyProperty(s[0]);
* }
*/
builder.keyProperty(ms.getKeyProperty());
//setStatementTimeout()
builder.timeout(ms.getTimeout());
//setStatementResultMap()
builder.parameterMap(ms.getParameterMap());
//setStatementResultMap()
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
//setStatementCache()
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
以上是我遇到的两个问题,更换包后,还需要更多的测试才可以部署到生产环境,否则客户们会疯的,可见,新包有风险,更换需谨慎哈!