mybatis源码阅读
配置解析
首先来看一个简单使用例子
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);
...
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
...
}
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 multipleResults, ResultMapping parentMapping),然后调用handleRowValues,最后调用handleRowValuesForSimpleResultMap方法一行行获取数据。
- 返回类型实例化
DefaultResultSetHandler.createResultObject()
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
//是否时基本类型或者有配置类型handler
if (hasTypeHandlerForResultObject(rsw, resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
//反射创建
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
//构造方法映射直接创建
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
DefaultObjectFactory,通过反射创建返回类型实例
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
//无参
Constructor<T> constructor constructor = type.getDeclaredConstructor();
return constructor.newInstance();;
if (constructorArgTypes == null || constructorArgs == null) {
//有参
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
return constructor.newInstance(constructorArgs.toArray(new Object[0]))
}
- 数据属性值注入
数据赋值,根据查询的列进行bean数据的赋值。columns来源于ResultSet。这里是以数据库ResultSet查询
ResultSetWrapper初始化的时候会获取到列名
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
super();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
final ResultSetMetaData metaData = rs.getMetaData();
final int columnCount = metaData.getColumnCount();
//获取所有的列
for (int i = 1; i <= columnCount; i++) {
columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
classNames.add(metaData.getColumnClassName(i));
}
}
来看具体一行数据赋值处理方法getRowValue
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
//是否走自动映射,这里会根据配置的autoMappingBehavior属性进行判断
if (shouldApplyAutomaticMappings(resultMap, false)) {
//自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
//最后显示映射兜底,这里就是根据resultmap配置的property和colum映射关系进行赋值
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
//最后根据配置的returnInstanceForEmptyRow属性判断,如果查询为空是返回空对象还是null
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
自动映射逻辑,这里就是根据列名找属性名的过程
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
if (autoMapping == null) {
autoMapping = new ArrayList<>();
//获取所有查询的列名
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
//前缀处理
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified,
// ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
//返回类型对象是否有该属性
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {//有该属性并且有set方法
if (resultMap.getMappedProperties().contains(property)) {//跳过已经显式映射过的属性
continue;
}
//获取set方法入参类型,判断是否有类型处理器
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
//找到TypeHandler,添加到自动类型映射map里
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {//自动映射未知列处理
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else {//未知字段处理
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}
//加入缓存中
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
最后返回的autoMapping是{列名,属性名,typeHandler},回到applyAutomaticMappings方法里对属性进行赋值
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
//使用typeHandler从rs里获取对应属性的value值
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
自动注入值完成后,会根据resultMap进行映射调用applyPropertyMappings方法赋值。这里就不用多看了,因为resultMap里已经明确了column和property的映射关系。最后找不到对应的column会根据配置的autoMappingBehavior属性来确定如何处理。
总结
mybatis初始化会load所有的配置信息到Configuration对象中。在获取mapper接口的时候是返回其动态代理对象,代理的handler是MapperProxy类,其invoker方法执行会构造一个PlainMethodInvoker对象,最后会调用MapperMethod.execute()方法,execute方法会根据不同的command(增删改查)来构造不同的PrepareStatement,最后都是通过PrepareStatement来执行具体的sql。如果是查询返回值会根据resultMap映射进行colum到property的赋值,如果开启了自动映射,对未明确的列会先进行自动注入属性值。前提是colum和property名称相等。