参考:mybatis源码中文注释
https://github.com/tuguangquan/mybatis
mybatis运行流程图:
先看一段mybatis的连接数据库操作的代码:下面跟着源码走一遍运行流程
public static void main( String[] args ) throws IOException {
String resource="SqlMapConfig.xml"; //1.数据源,别名等信息的配置文件
InputStream inputStream= Resources.getResourceAsStream(resource); //2.调用类加载器,加载配置文件中的类,并以二进制形式赋给inputStream
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);//3.根据inputStream得到SqlsessionFactory,过程描述如下@SqlsessionFactory
SqlSession sqlSession=sqlSessionFactory.openSession(); //4.通过SqlsessionFactory产生Sqlsession实例(里面阐述的生成事务【进行事务的提交】,执行器【执行sql】)
Book book=sqlSession.selectOne("test.findBookById",1000); //5.调用sqlsession的crud方法
// System.out.println(book);
System.out.println(String.valueOf(book));
sqlSession.close();
}
}
以下是mybatis的第三步得实现 过程:
new SqlSessionFactoryBuilder().build(inputStream);--------------------------------------》@SqlsessionFactory第一步,加载配置文件并转成二进制InputStream流
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);-----@SqlsessionFactory第二步:解析xml配置文件,将结果封装进Configuration
(SqlSessionFactory) return build(parser.parse());------------------------------------------》@SqlsessionFactory第三步 解析xml的每个节点,/configuration,/environments,/dataSource..........
return new DefaultSqlSessionFactory(Configuration); -------@SqlsessionFactory第四步,将解析得到的configuration赋给SqlSessionFactory接口的实现类DefaultSqlSessionFactory的成员变量configuration对象,并且返回该实现类DefaultSqlSessionFactory,再向上转型就得到SqlsessionFactory
parser.parse()得到的是一个 Configuration类 parse()方法解析的是SqlMapConfig.xml文件,依次从根节点
configuration【
parseConfiguration(parser.evalNode("/configuration"));
】,到子节点 environments 【
environmentsElement(root.evalNode("environments"));
】【dataSource【
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
】】 ,mappers【
mapperElement(root.evalNode("mappers"));
-----------------------------------------------------------------------------------------
//至此SqlMapConfig.xml解析完成,解析得到的内容全在Configuration里面
return new DefaultSqlSessionFactory(Configuration);
//注意:
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
小插曲:
费尽千辛万苦,只为拿到Configuration【xml配置文件---------到---------configuration配置对象的过程】,后续要从里面拿到
final Environment environment = configuration.getEnvironment();
final Executor executor = configuration.newExecutor(tx, execType); ------13号技师(executor 执行器)粉墨登场
Environment里面主要是:三个东西
//环境id
private final String id;
//事务工厂
private final TransactionFactory transactionFactory;
//数据源
private final DataSource dataSource;
下面是第四步的描述:
//最终都会调用2种方法:openSessionFromDataSource,openSessionFromConnection
//以下6个方法都会调用openSessionFromDataSource
public class DefaultSqlSessionFactory implements SqlSessionFactory {
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
/**
*openSessionFromDataSource主要是通过configuration里的信息生成事务对象,
*生成Executor执行器对象(执行sql),进行事务的自动的提交
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//通过事务工厂来产生一个事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//生成一个执行器(事务包含在执行器里)
final Executor executor = configuration.newExecutor(tx, execType);
//然后产生一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit); //----------返回sqlsession对象,程序员可以通过该接口对象调用相应的方法进行对数据库的CRUD
} catch (Exception e) {
//如果打开事务出错,则关闭它
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
//最后清空错误上下文
ErrorContext.instance().reset();
}
}
}
下面是第五步:sqlsession的crud(增删改查)方法的实现:(以selectOne()的方法举例说明)
public interface SqlSession extends Closeable {
<T> T selectOne(String statement);
}
DefaultSqlSession实现了Sqlsession接口:(里面还描述了selectMap,selectList,insertupdate,delete,commit,rollback,close)
@Override
public <T> T selectOne(String statement) {
return this.<T>selectOne(statement, null);
}
具体实现过程:(实际它调用了selectList)
//核心selectOne
@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,很简单的,如果得到0条则返回null,得到1条则返回1条,得到多条报TooManyResultsException错
// 特别需要主要的是当没有查询到结果的时候就会返回null。因此一般建议在mapper中编写resultType的时候使用包装类型
//而不是基本类型,比如推荐使用Integer而不是int。这样就可以避免NPE
List<T> list = this.<T>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;
}
}
selectList的实现:
//核心selectList
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据statement id找到对应的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement); //---------这里是根据方法的id查询要调用mapper.xml里面的哪一个sql语句。
//@executor下文描述:转而用执行器来查询结果,注意这里传入的ResultHandler是null
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);// wrapCollection(parameter)完成输入参数集的封装和对占位符值的设置,不如传的id=1,则where id=? 经过这条语句后就变成了where id=1,然后将sql语句发给数据库执行,再回调函数中处理数据库返回来的结果集
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
下面详细说明一下@executor执行器的实现:
//查询,带分页
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
BaseExecutor implements Executor实现Executor执行器:
//SqlSession.selectList会调用此方法
public abstract class BaseExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//得到绑定sql
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//查询
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
}
追踪了这么久,终极boss终于被我们找出来了:
public abstract class BaseExecutor implements Executor {
@SuppressWarnings("unchecked")
@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.");
}
//先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
//加一,这样递归调用到上面的时候就不会再清局部缓存了
queryStack++;
//先根据cachekey从localCache去查
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//若查到localCache缓存,处理localOutputParameterCache
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
//如果是STATEMENT,清本地缓存
clearLocalCache();
}
}
return list;
}
}
接着我们看看mybatis是整么从数据库查数据的 @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 {
/**
*以下几步是mybatis实现jdbc的规范的操作
*Connection connection = getConnection(ms.getStatementLog()); Connenct con=DriverManage
stmt = handler.prepare(connection);
handler.parameterize(stmt);
*/
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//最后删除占位符
localCache.removeObject(key);
}
//加入缓存
localCache.putObject(key, list);
//如果是存储过程,OUT参数也加入缓存
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
5.1:重点来看BatchExecutor(实现Executor接口)的doQuery(.........)方法:
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
/*
Connection con=DriverManager.getConnection("localhost:3306/test","root","root");
String sql="select id from user where id=?";
PrepareStatement prepareStat=con.prepareStatment(sql);
prepareStat.setInt(1,666);
ResultSet rs=prepareStat.executeQuery();
while(rs.next()){
System.out.println(rs.getInt(1));
//System.out.println(rs.getString('column_name'))
// Date birthday=rs.getDate(8);
//System.out.println(rs.getString(2));
//int a=rs.getInt('column_name'));
}
prepareStat.close();
con.close();
*/
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
5.1.1 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);------------------------parameterObject是客户端发过来的输入集参数,newStatementHandler方法完成了输入参数的封装,下面我们再来看看myatis是整么实现输入参数的 封装的:,进入到这个方法去看看。它是位于Configuration.java里面:
//创建语句处理器
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建路由选择语句处理器--------------------进去看一看
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//插件在这里插入
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
Configuration.java:
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根据语句类型,委派到不同的语句处理器(STATEMENT|PREPARED|CALLABLE)
switch (ms.getStatementType()) {
case STATEMENT:
//----------------------------进去看一看,是个啥
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
SimpleStatementHandler.java: -----------走到它的父类去看看------------->BaseStatementHandler.java
/**
* @author Clinton Begin
public class SimpleStatementHandler extends BaseStatementHandler {}
*/
/**
* 语句处理器的基类
*
*/
public abstract class BaseStatementHandler implements StatementHandler {
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
//------------------------------>该句完成了封装,我们进去看看是个啥@mappedStatement.getBoundSql(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
//生成parameterHandler
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
//生成resultSetHandler
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
}
@ mappedStatement.getBoundSql(parameterObject);重点来了: -----------------------MappedStatement.java
/**
* @author Clinton Begin
*/
/**
* 映射的语句
*
*/
public final class MappedStatement {
public BoundSql getBoundSql(Object parameterObject) {
//其实就是调用sqlSource.getBoundSql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
//剩下的可以暂时忽略
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
}
5.1.2 其中完成结果集的封装----------------------------------> return handler.<E>query(stmt, resultHandler); 进入这个方法我们看看:
public class CallableStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
List<E> resultList = resultSetHandler.<E>handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}
}
关注 List<E> resultList = resultSetHandler.<E>handleResultSets(cs); 我们进去看看(DefaultResultSetHandler 里面还实现了)------------------------输出结果的映射与封装
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
//一般resultMaps里只有一个元素
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResulSets();
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 Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
//实例化ResultLoaderMap(延迟加载器)
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//调用自己的createResultObject,内部就是new一个对象(如果是简单类型,new完也把值赋进去)
Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
//一般不是简单类型不会有typehandler,这个if会进来
final MetaObject metaObject = configuration.newMetaObject(resultObject);
boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
if (shouldApplyAutomaticMappings(resultMap, false)) {
//自动映射咯
//这里把每个列的值都赋到相应的字段里去了
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
resultObject = foundValues ? resultObject : null;
return resultObject;
}
return resultObject;
}
}
至此,解析SqlMapConfig.xml--------------解析Mapper.xml---------------------->调用指定了id的mapper.xml里面的方法------->parameterType输入参数集的封装------------------>resultType输出结果集的封装,代码追踪完毕。