mybatis中文文档:https://mybatis.org/mybatis-3/zh/
1. Mybatis执行流程及核心组件
组件作用如下:
- Configuration:用于描述Mybatis的主要配置信息,其他组件需要获取配置信息时,直接通过Configuration对象获取。除此之外,Mybatis在应用启动时,将Mapper配置信息、类型别名、TypeHandler等注册到Configuration组件中。
- MappedStatement:用于描述Mapper中的SQL配置信息,是对Mapper XML配置文件中<select|update|insert|delete>等标签或@Select/@Update等注解配置信息的封装。
- SqlSession:Mybatis提供的面向用户的API,表示和数据库交互时的会话对象,用于完成数据库的增删改查功能。SqlSesssion是Executor组件的外观,目的是对外提供易于理解和使用的数据库操作接口。
- Executor:是Mybatis的SQL执行器,Mybatis中对数据库所有增删改查操作都由其完成。
- StatementHandler:封装了对JDBC Statement对象的操作,比如为Statement对象设置参数,调用Statement接口提供的方法与数据库交互等。
- ParameterHandler:当Mybatis框架使用的Statement类型为CallableStatement和PreparedStatement时,ParameterHandler用于为Statement对象参数占位符设置值。
- ResultSetHandler:封装了对JDBC中的ResultSet对象操作,当执行SQL类型为SELECT语句时,ResultSetHandler用于将查询结果转换成Java对象。
- TypeHandler:类型处理器,用于处理Java类型与JDBC类型之间的映射,他的作用主要体现在能够根据Java类型调用PreparedStatement或CallableStatement对象对应的setXXX()方法为Statement对象设置值,而且能够根据Java类型调用ResultSet对象对应的getXXX()获取SQL执行结果。
2. 源码部分(mybatis 3.5.3)
mybatis-config.xml配置
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/francis?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="org.apache.ibatis.submitted.use_actual_param_name.Mapper"/>
<mapper class="org.apache.ibatis.submitted.use_actual_param_name.StudentMapper"/>
<!-- 通过package指定Mapper接口所在包名 -->
<!--<package name="org.apache.ibatis.submitted.use_actual_param_name"/>-->
<!-- 通过resource指定Mapper.xml的classpathl路径 -->
<!--<mapper resource="org.apache.ibatis.submitted.use_actual_param_name.Mapper.xml"></mapper>-->
<!-- 通过url指定Mapper.xml的网络路径 -->
<!--<mapper url="file:///var/mappers/Mapper.xml"></mapper>-->
</mappers>
</configuration>
2.1 Configuration的实例化、Mapper接口和MappedStatement的注册
入口:
private static SqlSessionFactory sqlSessionFactory;
@BeforeAll
static void setUp() throws Exception {
// 1. 以mybatis-config.xml配置文件输入流作为参数
try (Reader reader = Resources.getResourceAsReader(
"org/apache/ibatis/submitted/use_actual_param_name/mybatis-config.xml")) {
// 2. 进入build方法,实例化SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
"org/apache/ibatis/submitted/use_actual_param_name/CreateDB.sql");
}
进入 org.apache.ibatis.session.SqlSessionFactoryBuilder.java
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// 1. 创建XMLConfigBuilder 对象
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 2. 进入parse(),这个方法实例化了Configuration对象
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
进入org.apache.ibatis.builder.xml.XMLConfigBuilder.java
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 1. 解析配置文件
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
// 用于配置属性信息,属性值可以通过${...}方式引用
propertiesElement(root.evalNode("properties"));
// 通过一些属性控制mybatis运行时的行为,如果指定日志实现,默认的Executor等
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 配置类型别名
typeAliasesElement(root.evalNode("typeAliases"));
// 注册用户自定义的插件
pluginElement(root.evalNode("plugins"));
// mybatis通过ObjectFactory创建参数对象和结果集映射对象。<objectFactory>标签用于配置自定义的ObjectFactory。
objectFactoryElement(root.evalNode("objectFactory"));
// mybatis通过ObjectWrapperFactory创建ObjectWrapper对象。<objectWrapperFactory>标签用于配置自定义的ObjectWrapperFactory
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// mybatis通过ReflectorFactory创建描述Java类型反射信息的Reflector对象。<reflectorFactory>标签用于配置自定义的ReflectorFactory。
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// 用于配置mybatis数据库连接相关的环境及事务管理器信息
environmentsElement(root.evalNode("environments"));
// 根据不同数据库厂商执行不同的sql语句
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
// 2. 解析<mappers></mappers>,进入mapperElement()
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// <mappers></mappers>标签中配置Mapper接口或Mapper.xml共有4种方式,上文配置文件中有提到
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// 3. 我这里配置的class方式,进入addMapper()
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
进入org.apache.ibatis.session.Configuration.java 》org.apache.ibatis.binding.MapperRegistry.java
// private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 1. 根据Mapper接口创建MapperProxyFactory对象,并注册到knownMappers
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 2. 进入parse()
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
进入org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.java
public void parse() {
String resource = type.toString();
// 1. 判断有没有加载过
if (!configuration.isResourceLoaded(resource)) {
// 2. 加载Mapper接口对应的Mapper.xml
loadXmlResource();
// 3. 将资源路径添加到Configuration中
configuration.addLoadedResource(resource);
// 4. 设置namespace
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
// 5. 解析sql语句
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
// 这个方法主要是解析xml
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
String xmlResource = type.getName().replace('.', '/') + ".xml";
// #1347
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
// Search XML mapper that is not in the module but in the classpath.
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required
}
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
// 解析Mapper.xml,重点关注<insert|update|insert|delete>的解析,最终也会调用MapperBuilderAssistant.addMappedStatement()方法,下文有提到
xmlParser.parse();
}
}
}
// 解析sql语句
void parseStatement(Method method) {
Class<?> parameterTypeClass = getParameterType(method);
LanguageDriver languageDriver = getLanguageDriver(method);
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
// 获取Options 注解,此注解可以设置一些参数,比如设置在插入操作时返回表的主键
Options options = method.getAnnotation(Options.class);
// 以前的版本通过mappedStatementId 调用底层方法,新版本也支持
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = configuration.getDefaultResultSetType();
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = !isSelect;
boolean useCache = isSelect;
KeyGenerator keyGenerator;
String keyProperty = null;
String keyColumn = null;
// 这一段其实就是判断在插入或修改操作时需不需要返回表的主键
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// first check for SelectKey annotation - that overrides everything else
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else if (options == null) {
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
} else {
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
} else {
keyGenerator = NoKeyGenerator.INSTANCE;
}
// 这些都是可在Options注解中配置的
if (options != null) {
if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
flushCache = true;
} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
flushCache = false;
}
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
if (options.resultSetType() != ResultSetType.DEFAULT) {
resultSetType = options.resultSetType();
}
}
String resultMapId = null;
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
resultMapId = String.join(",", resultMapAnnotation.value());
} else if (isSelect) {
resultMapId = parseResultMap(method);
}
// 这里其实就是注册MappedStatement对象,MappedStatement维护的就是<insert|update|insert|delete>或@Select|@Insert等sql配置信息
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);
}
}
2.2 Mapper方法调用过程
主要以查询为例
@Test
void tests() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
// 已经通过addMapper()方法注册到HashMap,key就是class,这里拿到的是一个代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
// 通过断点调试进入getStudent()方法,按F5进入
Student student = mapper.getStudent("1001", "oyc");
assertNotNull(student);
}
}
进入org.apache.ibatis.binding.MapperProxy.java
这个类实现了InvocationHandler(这里是JDK动态代理方式,还有CGLIB方式),用于实现动态代理相关逻辑
// 调用动态代理对象方法时会调用这个方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 1. 进入cachedMapperMethod(),里面做了很多执行sql前的准备
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 2. 进入execute()
return mapperMethod.execute(sqlSession, args);
}
通过cachedMapperMethod()进入org.apache.ibatis.binding.MapperMethod.java
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// 这里重点关注new MethodSignature()
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
// 这一段是判断返回类型
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
// 返回类型为Map时,可通过@MapKey注解指定map的key
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
// RowBounds参数索引位置,用于处理分页查询
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
// ResultHandlercar参数位置,用于处理从数据库中检索的每一行记录
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 1. 重点关注new ParamNameResolver(),这里完成了参数的解析
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
进入org.apache.ibatis.reflection.ParamNameResolver.java
public ParamNameResolver(Configuration config, Method method) {
final Class<?>[] paramTypes = method.getParameterTypes();
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
// 这里就是判断Mapper接口中定义的方法的参数有没有加@Param注解
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
// 没有加@Param注解时,判断是否开启了使用真实参数名,默认开启
if (config.isUseActualParamName()) {
// 1. 进入getActualParamName()
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
private String getActualParamName(Method method, int paramIndex) {
return ParamNameUtil.getParamNames(method).get(paramIndex);
}
进入org.apache.ibatis.reflection.ParamNameUtil.java
// 上一步进来调用这个方法
public static List<String> getParamNames(Method method) {
return getParameterNames(method);
}
// 最终调用这个方法,我们会发现Method extends Executable
private static List<String> getParameterNames(Executable executable) {
// 进入executable.getParameters()
return Arrays.stream(executable.getParameters()).map(Parameter::getName).collect(Collectors.toList());
}
进入java.lang.reflect.Executable.java
public Parameter[] getParameters() {
// TODO: This may eventually need to be guarded by security
// mechanisms similar to those in Field, Method, etc.
//
// Need to copy the cached array to prevent users from messing
// with it. Since parameters are immutable, we can
// shallow-copy.
return privateGetParameters().clone();
}
private Parameter[] privateGetParameters() {
// Use tmp to avoid multiple writes to a volatile.
Parameter[] tmp = parameters;
if (tmp == null) {
// Otherwise, go to the JVM to get them
try {
tmp = getParameters0();
} catch(IllegalArgumentException e) {
// Rethrow ClassFormatErrors
throw new MalformedParametersException("Invalid constant pool index");
}
// If we get back nothing, then synthesize parameters
if (tmp == null) {
hasRealParameterData = false;
tmp = synthesizeAllParams();
} else {
hasRealParameterData = true;
verifyParameters(tmp);
}
parameters = tmp;
}
return tmp;
}
重新回到org.apache.ibatis.binding.MapperProxy.java
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 进入execute()
return mapperMethod.execute(sqlSession, args);
}
进入org.apache.ibatis.binding.MapperMethod.java
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 判断sql语句是增删改查中的哪种类型
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
// 我这里是SELECT类型,重点就看这里的代码
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
// 1. 拿到方法入参
Object param = method.convertArgsToSqlCommandParam(args);
// 2. 开始执行查询了
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
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;
}
public Object convertArgsToSqlCommandParam(Object[] args) {
// 这里面就是去拿参数
return paramNameResolver.getNamedParams(args);
}
通过上面sqlSession.selectOne()进入org.apache.ibatis.session.defaults.DefaultSqlSession.java
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
// 实际上是调用selectList(),最后返回第一条记录或者返回null
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
// 先调用这个方法
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
// 再调用这个方法
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 前面已经注册过MappedStatement ,直接获取
MappedStatement ms = configuration.getMappedStatement(statement);
// 这里重点关注query()
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
进入org.apache.ibatis.executor.BaseExecutor.java
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 重点关注
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 进入queryFromDatabase()
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 进入doQuery()
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
进入org.apache.ibatis.executor.SimpleExecutor.java
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 重点关注prepareStatement()
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
// 进入parameterize(),一步步调试
handler.parameterize(stmt);
return stmt;
}
最终进入org.apache.ibatis.scripting.defaults.DefaultParameterHandler.java
// 执行完这个方法就能得到形如:select * from student where id = 1 and name = 'francis' 的sql语句
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
// 获取Mapper接口中方法的入参值
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 将入参值替换占位符
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
回到org.apache.ibatis.executor.SimpleExecutor.java
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
// 进入query()
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
通过断点调试,进入org.apache.ibatis.executor.statement.PreparedStatementHandler.java
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 1.
ps.execute();
// 2.
return resultSetHandler.handleResultSets(ps);
}
进入com.mysql.cj.jdbc.ClientPreparedStatement.java
这里面是JDBC厂商(此处是mysql)提供的接口,实际上就是JDBC操作数据库
public boolean execute() throws SQLException {
try {
synchronized(this.checkClosed().getConnectionMutex()) {
JdbcConnection locallyScopedConn = this.connection;
if (!this.doPingInstead && !this.checkReadOnlySafeStatement()) {
throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), "S1009", this.exceptionInterceptor);
} else {
ResultSetInternalMethods rs = null;
this.lastQueryIsOnDupKeyUpdate = false;
if (this.retrieveGeneratedKeys) {
this.lastQueryIsOnDupKeyUpdate = this.containsOnDuplicateKeyUpdateInSQL();
}
this.batchedGeneratedKeys = null;
this.resetCancelledState();
this.implicitlyCloseAllOpenResults();
this.clearWarnings();
if (this.doPingInstead) {
this.doPingInstead();
return true;
} else {
this.setupStreamingTimeout(locallyScopedConn);
Message sendPacket = ((PreparedQuery)this.query).fillSendPacket();
String oldDb = null;
if (!locallyScopedConn.getDatabase().equals(this.getCurrentDatabase())) {
oldDb = locallyScopedConn.getDatabase();
locallyScopedConn.setDatabase(this.getCurrentDatabase());
}
CachedResultSetMetaData cachedMetadata = null;
boolean cacheResultSetMetadata = (Boolean)locallyScopedConn.getPropertySet().getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue();
if (cacheResultSetMetadata) {
cachedMetadata = locallyScopedConn.getCachedMetaData(((PreparedQuery)this.query).getOriginalSql());
}
locallyScopedConn.setSessionMaxRows(((PreparedQuery)this.query).getParseInfo().getFirstStmtChar() == 'S' ? this.maxRows : -1);
rs = this.executeInternal(this.maxRows, sendPacket, this.createStreamingResultSet(), ((PreparedQuery)this.query).getParseInfo().getFirstStmtChar() == 'S', cachedMetadata, false);
if (cachedMetadata != null) {
locallyScopedConn.initializeResultsMetadataFromCache(((PreparedQuery)this.query).getOriginalSql(), cachedMetadata, rs);
} else if (rs.hasRows() && cacheResultSetMetadata) {
locallyScopedConn.initializeResultsMetadataFromCache(((PreparedQuery)this.query).getOriginalSql(), (CachedResultSetMetaData)null, rs);
}
if (this.retrieveGeneratedKeys) {
rs.setFirstCharOfQuery(((PreparedQuery)this.query).getParseInfo().getFirstStmtChar());
}
if (oldDb != null) {
locallyScopedConn.setDatabase(oldDb);
}
if (rs != null) {
this.lastInsertId = rs.getUpdateID();
this.results = rs;
}
return rs != null && rs.hasRows();
}
}
}
} catch (CJException var11) {
throw SQLExceptionsMapping.translateException(var11, this.getExceptionInterceptor());
}
}
回到resultSetHandler.handleResultSets(ps);
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 进入handleResultSets()
return resultSetHandler.handleResultSets(ps);
}
进入org.apache.ibatis.executor.resultset.DefaultResultSetHandler.java
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 1. 获取ResultSet对象,并包装成ResultSetWrapper
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 2. 获取ResultMap信息,一般只有一个ResultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 3. 调用handleResultSet()处理结果集
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}