sqlSession一共俩个实现类,我们这里分析默认的DefaultSqlSession类
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//ms对象代表我们的xml中的一条sql。例如:
MappedStatement ms = configuration.getMappedStatement(statement);
//如果是二级缓存执行器,就是多了维护二级缓存的操作。我们这里查看最简单的执行器
//SimpleExecutor
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();
}
}
// 三种普通的执行器,都是继承了BaseExecutor类
@Override
public List 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);
}
//CachingExecutor执行器,里面的被包装执行器,最终调用这个方法。因为在CachingExecutor类的
//query方法中,已经生成了BoundSql ,CacheKey 。所以直接调用这个方法
@Override
public List 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 list;
try { //一级缓存(默认是session级别的,和我们的sqlSession对象绑定)的使用,从一级缓存中查询,不存在数据,则查询数据库
queryStack++;
list = resultHandler == null ? (List) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else { //直接查询数据库
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 List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {//这里将查询的逻辑交给了具体的子类,使用的模板方法的设计模式。同时我们的执行器这块
//也是一个典型的包装设计模式。二级缓存执行器包装下面的三种基本执行器
//我们查看SimpleExecutor执行器的doDuery方法
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 List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//如果当前执行器有它的包装类,那么wrapper只能是CachingExecutor,否则就是它自己
//这里默认是返回PreparedStatementHandler类型的statement对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//进行了连接的建立,statenment对象的创建,参数的赋值操作。(默认PreparedStatementHandler
//其他的statement步骤差不多)
stmt = prepareStatement(handler, ms.getStatementLog());
//进行数据库的操作,并对结果进行处理为java类型 比如:preparedStatement.execute();
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
我们的StatementHandler的类结构和我们的执行器的几乎一模一样。很明显这儿也是用到了装饰设计模式,同时也用到了模板方法。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
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());
}
为何我们常说的四大对象,是可以进行插件拦截。就是因为我们创建好的对象,最终都会经过代理,生成代理对象。
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
executor = (Executor) interceptorChain.pluginAll(executor);
查看生成ms对象代码,发现我们的StatementType默认是预编译的。
在MyBatis实现了statementHandler的有四个类:
RoutingStatementHandler,这是一个封装类,它是其他三个的包装类,它不提供具体的实现,只是根据StatementType,创建不同的类型StatementHandler。
SimpleStatementHandler,这个类对应于JDBC的Statement对象,用于没有预编译参数的SQL的运行。
PreparedStatementHandler 这个用于预编译参数SQL的运行,默认的就是预编译。
CallableStatementHandler 存储过程的调度。
stmt = prepareStatement(handler, ms.getStatementLog());
//相当于以前我们的jdbc
// connection = JDBCTools.getConnection();
// preparedStatement = connection.prepareStatement(sql);
//preparedStatement.setString(1, "ATGUIGU"); 赋值操作
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
//这里不同的handler,返回不同的statement,我们这儿使用默认的,预编译的handler,生成预编译statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
//查看代码,由于使用的是PooledDataSource,在获取连接对象的时候,会进行一次代理
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
//这儿的connection 是一个代理对象
if (statementLog.isDebugEnabled()) {
//如果是debug,则再次进行一次代理,好插入记录日志的功能
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
//使用jdk的动态代理
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
@Override
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
if ("prepareStatement".equals(method.getName())) {
//如果是执行的 prepareStatement()方法
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
//对PrepareStatement进行代理
// preparedStatement = connection.prepareStatement(sql); 返回的是一个代理对象
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("createStatement".equals(method.getName())) {
Statement stmt = (Statement) method.invoke(connection, params);
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else {
return method.invoke(connection, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
我们的connection对象,确实被代理了俩次,所以为了性能考虑,我们线上不要用debug模式。
//这一行代码,相当于我们jdbc的
//preparedStatement = connection.prepareStatement(sql);
stmt = handler.prepare(connection, transaction.getTimeout());
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
//默认是执行这个逻辑,如果是dubug模式,这里返回的statement对象是一个代理对象。可以查看上面
//新建connnection对象的逻辑,所以生产环境不建议用debug模式
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
接下来就是参数赋值的操作:
handler.parameterize(stmt);
//对应jdbc的 preparedStatement.setString(1, "ATGUIGU"); 赋值操作
在这个方法中,我们主要做的工作就是,遍历我们的参数,然后将参数转换为jdbc类型的参数,然后进行赋值操作。在这里会涉及到类型推断,默认类型。所以,为了性能,我们最好在xml中,指明jdbcType和JavaType。这样直接找到对应的类型处理器,提高效率。
然后,我们返回的就是一个已经完成connection建立,statement对象创建,参数赋值好的statement对象。接下来就是我们的doQuery方法中的:
return handler.query(stmt, resultHandler);
进行查询数据库的操作。