mybatis核心組件之MapperMethod
註釋: 跟蹤
mapper
執行接口方法到數據庫執行sql
語句的源碼過程
// mybatis-spring
UserMapper mapper = context.getBean(UserMapper.class);
System.out.println(mapper.selectAny());
可以看到mapper
是MapperProxy產生的代理類,那麼MapperProxy中可定有invok
方法對目標方法進行了增強處理
MapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 如果方法是Object類的方法,則直接反射執行
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
// 獲取MapperMethod
MapperMethod mapperMethod = this.cachedMapperMethod(method);
// 執行sql語句
return mapperMethod.execute(this.sqlSession, args);
}
}
- 先判斷執行的方法是不是Object類的方法,比如
tostring
,hashcode
等方法,是的話則直接反射執行這些方法 - 如果不是,從緩存中獲取MapperMethod,如果爲空則創建並加入緩存,然後執行
sql
語句
private MapperMethod cachedMapperMethod(Method method) {
// 根據方法從緩存中獲取
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
// 不存在則創建一個
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
// 放入緩存
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
MapperMethod
構造函數
private final MapperMethod.SqlCommand command;
private final MapperMethod.MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
SqlCommand一個內部類 封裝了SQL
標籤的類型 insert
update
delete
select
MethodSignature一個內部類 封裝了方法
的參數信息
返回類型
信息等
execute執行
判斷sql
的執行類型,執行相應的方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) { // 返回類型爲void
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) { // 返回類型爲集合或數組
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {// 由@MapKey控制返回
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {// 返回類型爲Cursor<T>,採用遊標
result = this.executeForCursor(sqlSession, args);
} else {
// 其他類型
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
抽出這一段代碼跟蹤一下源碼
// 其他類型
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
convertArgsToSqlCommandParam解析入參
ParamNameResolver.getNamedParams
public Object convertArgsToSqlCommandParam(Object[] args) {
return this.paramNameResolver.getNamedParams(args);
}
解析@Param註解
names
是SortedMap,在構造函數中賦值的,判斷入參有沒有@Param
註解。
key是入參的順序,從0開始;value是@Param
中的值
如果沒有@Param
註解,則value值爲arg0
、arg1
…
將入參名與值匹配
public Object getNamedParams(Object[] args) {
int paramCount = this.names.size();
if (args != null && paramCount != 0) {
// 只有一個入參時,返回入參值
if (!this.hasParamAnnotation && paramCount == 1) {
return args[((Integer)this.names.firstKey()).intValue()];
} else {
// 多個入參時,返回一個Map
Map<String, Object> param = new ParamMap();
int i = 0;
for(Iterator i$ = this.names.entrySet().iterator(); i$.hasNext(); ++i) {
Entry<Integer, String> entry = (Entry)i$.next();
param.put(entry.getValue(), args[((Integer)entry.getKey()).intValue()]);
String genericParamName = "param" + String.valueOf(i + 1);
if (!this.names.containsValue(genericParamName)) {
param.put(genericParamName, args[((Integer)entry.getKey()).intValue()]);
}
}
return param;
}
} else {
return null;
}
}
示例1: 多個入參,沒有加@Param註解
@Select("SELECT count(0) from es_inter_invokfaillog where rownum = #{num} and invok_type=#{type}")
public Integer selectAny(int num,String type);
這樣執行sql
會報錯,找不到num
,可以改爲#{arg1}
或#{param1}
示例2:多個入參,加@Param註解
@Select("SELECT count(0) from es_inter_invokfaillog where rownum = #{num} and invok_type=#{type}")
public Integer selectAny(@Param("num")int num,@Param("type")String type);
示例3:多個入參,部分加@Param註解,部分不加
@Select("SELECT count(0) from es_inter_invokfaillog where rownum = #{num} and invok_type=#{arg1}")
public Integer selectAny(@Param("num")int num,String type);
提示:只有一個入參時不存在這些問題,因爲一個入參直接返回的是它的值,是一個String不是Map
執行SqlSessionTemplate中的方法
這裏的sqlSession
就是SqlSessionTemplate,mybatis與spring的整合之SqlSessionTemplate
result = sqlSession.selectOne(this.command.getName(), param);
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.selectOne(statement, parameter);
}
sqlSessionProxy
是動態代理生成的,每一次執行方法時都會重新去 new
一個DefaultSqlSession,可以看下invoke
方法部分代碼
// 獲取session,這裏有個事物的判斷
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
// 真正執行sql語句的地方
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
unwrapped = result;
}