MyBatis源码分析
使用注解方式执行SQL
使用注解执行SQL只需在自定义Mapper接口上添加注解如@Select、@Insert、@Update、@Delete等,示例如下:
public interface UserMapper {
@Select("select * from user where id = #{id}")
User selectUser(@Param("id") Integer id);
}
那么便可使用getMapper获取代理对象后执行接口方法得到sql查询结果,不需再在Mapper.xml文件中写SQL。接下来从源码进行分析注解形式执行SQL的原理,相比于前面两种方式,注解的源码相对就比较简单。
MyBatis支持的注解都在包org.apache.ibatis.annotations中,而对于增删改查(@Insert、@Delete、@Update、@Select)注解的解析都在MapperAnnotationBuilder类中。
在MapperAnnotationBuilder类中有个parse()方法用于对使用注解的Mapper接口方法进行解析转换,parse()方法源码如下:
从源码中可以看到parse()方法中主要包含以下步骤:
- 加载xml配置
- 缓存初始化
- 将使用注解的接口方法封装为MappedStatement对象
这里我们主要关注点在将使用注解的接口方法封装为MappedStatement对象上,也就是MapperAnnotationBuilder.parseStatement()方法。其源码如下(由于代码片段较长,这里只保留关键部分):
void parseStatement(Method method) {
Class<?> parameterTypeClass = getParameterType(method);
LanguageDriver languageDriver = getLanguageDriver(method);
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
// ......省略
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}
在parseStatement()方法中,利用反射获取了参数和注解中的SQL语句,然后将代码逻辑中构建好的各种对象(如sqlSource、statementType、sqlCommandType等)作为参数传给MapperBuilderAssistant.addMappedStatement()方法,来进行MappedStatement对象构建。在MapperBuilderAssistant.addMappedStatement()方法中,通过以下两句将构建好的MappedStatement传给Configuration对象
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
至此所有需要的配置已完成,从这里可以看出使用注解配置SQL的方式核心在于将原来从Mapper.xml解析封装MappedStatment对象的方式,改为从使用注解的方法上进行解析封装MappedStatment对象,之后的过程与前面两种调用方式相同,不再赘述。
相关参考
MyBatis源码分析——MyBatis核心组件和开启SqlSession
MyBatis源码分析——使用SqlSession操作数据库
MyBatis源码分析——调用Mapper接口方法执行SQL
MyBatis源码分析——使用注解执行SQL