配置解析
首先来看一个简单使用例子
String resource = "mybatis-config.xml";
//读取配置,创建sessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource));
//opensession
SqlSession sqlSession = sessionFactory.openSession();
//获取mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//执行接口方法
User user = mapper.findUserByNo("001");
下面会针对上面的步骤进行分析执行过程。读取配置会将配置文件中的配置读取到Configuration对象中进行存储。几个重要的属性
//存储已经解析的mapper文件类型,内部有map存储
MapperRegistry mapperRegistry;
//类型处理器注册表
TypeHandlerRegistry typeHandlerRegistry;
//类型昵称注册表
TypeAliasRegistry typeAliasRegistry;
//存储配置的增删改查操作语句
Map<String, MappedStatement> mappedStatements;
//resultMap映射集
Map<String, ResultMap> resultMaps;
//缓存集
Map<String, Cache> caches;
...
//还有很多公共的setting配置信息
XMLConfigBuilder.parse()会解析配置文件,将配置信息存储到Configuration对象中。
sessionFactory在opensession的时候会将解析的configuration信息传递给session。然后session.getMapper会从mapperRegistry中检查mapper是否已注册,如果已注册返回mapper接口类型的代理类,否则抛出异常。
动态代理
所有的mapper都是接口,这个时候调用mapper接口方法时候就是用的动态代理构建实例对象。
通过session获取mapper实例就是构造代理的过程。session的getMapper方法会调用Configuration的getMapper然后是MapperRegistry的。
MapperRegistry.getMapper方法源码
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {//通过MapperProxyFactory获取代理实例对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory获取代理对象方法
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
//通过JDK的Proxy类构造代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
MapperProxy类实现了InvocationHandler接口,其invoker方法执行会构造一个PlainMethodInvoker对象,最后会调用MapperMethod.execute()方法,来完成我们mapper接口的具体实现。来看看这个execute方法。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
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:
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 {
Object param = method.convertArgsToSqlCommandParam(args);
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;
}
这个execute方法会根据增删改查不同的指令进行相应的操作。
执行SQL
sql指令的发出都是通过DefaultSqlSession方法。
查询类:selectOne和selectList。selectOne会通过selectList来完成
修改:insert和update最后都会调用update
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
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;
}
}
DefaultSqlSession.selectList()方法会调用Executor的query方法。session中的executor实例是CachingExecutor。
CachingExecutor.query(),
CachingExecutor会有一个SimpleExecutor类型的Executor来真正执行查询,SimpleExecutor继承自BaseExecutor,BaseExecutor实现了Executor。
方法源码:
BaseExecutor.queryFromDatabase()
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调用的SimpleExecutor类方法
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;
}
SimpleExecutor.doQuery
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();
//这里创建的handler是RoutingStatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
RoutingStatementHandler.query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//这里的delegate是PreparedStatementHandler
return delegate.query(statement, resultHandler);
}
PreparedStatementHandler.query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();//执行查询
//返回值处理
return resultSetHandler.handleResultSets(ps);
}
最后还是通过PreparedStatement执行的sql操作。
返回值处理
- 调用流程
以查询为例,DefaultResultSetHandler用来处理返回值。前面sql执行完后会调用DefaultResultSetHandler的handleResultSets(Statement stmt),实际处理方法在handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List