标签:
目的
系统中的业务接口需要调用MyBatis的SQL时,业务接口定义的参数不符合MyBatis自己内部的规范,那么就需要把业务接口的参数转换成MyBatis内部参数规,MapperProxy代理就完成了这一职责,下面就来分析一下。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
上面的invoke就是代理类回调的方法,而cachedMapperMethod方法,做了一个缓存。这里MapperMethod类的构造器与MapperMethod类的execute方法就是主要的逻辑,先来看一下MapperMethod构造器。
1 MapperMethod构造器

这里有2个类的实例化,根据图中的查询分析一下这2个类。

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {
// cn.vansky.schedule.time.menu.dao.MenuMapper.findMenuByUserId
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
} else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
if (configuration.hasStatement(parentStatementName)) {
ms = configuration.getMappedStatement(parentStatementName);
}
}
if (ms == null) {
throw new BindingException("Invalid bound statement (not found): " + statementName);
}
// cn.vansky.schedule.time.menu.dao.MenuMapper.findMenuByUserId
name = ms.getId();
// SELECT
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
public MethodSignature(Configuration configuration, Method method) throws BindingException {
// 返回值类型Class
// interface java.util.List
this.returnType = method.getReturnType();
// 有无返回值 true:无,false:有
// false
this.returnsVoid = void.class.equals(this.returnType);
// 返回类型是否是集合Collection或者是数组
// true
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
// 返回类型是Map,获取注解MapKey
// 以上方法调用,值为null
this.mapKey = getMapKey(method);
// 返回类型是否Map
// false
this.returnsMap = (this.mapKey != null);
// 参数是否有@Param注解
// true
this.hasNamedParameters = hasNamedParams(method);
// null
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
// null
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 获取参数列表
// 0 -> userId
this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
}
SqlCommand类获取处理的唯一标识及SQL语句类型,MethodSignature类对业务接口方法的入参类型及出参类型进行处理。
2 MapperMethed执行入口-->execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
// 新增
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
// 修改
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
// 删除
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
// 查询
if (method.returnsVoid() && method.hasResultHandler()) {
// 无返回值void
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 集合Collection或数组
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// Map
result = executeForMap(sqlSession, args);
} else {
// 唯一结果
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method ‘" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
增(INSERT)、改(UPDATE)、删(DELETE),这3种操作可以归纳为一种,首先对业务接口的实际入参转成MyBatis内部参数,然后调用SqlSession的处理方法,最后对结果进行处理,返回结果。
查(SELECT)有4中情况。
一:无返回值
二:返回是集合Collection或者数组
三:返回是Map
四:返回一个结果
4种情况的具体细节不再分析。
总结
综上首先需要先把业务接口的实际入参转成MyBatis内部的参数,然后调用SqlSession相应的处理方法,最后对返回结果进行处理,在返回给业务接口。
连接MyBatis内部SqlSession与业务接口的代理类MapperProxy
标签:
原文地址:http://my.oschina.net/u/1269959/blog/524486