MyBatis源码的学习(15)---sqlSession.selectList⽅法
sqlSession⼀共俩个实现类,我们这⾥分析默认的DefaultSqlSession类
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//ms对象代表我们的xml中的⼀条sql。例如:<select>...</select>
MappedStatement ms = MappedStatement(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 <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);
}
//CachingExecutor执⾏器,⾥⾯的被包装执⾏器,最终调⽤这个⽅法。因为在CachingExecutor类的
//query⽅法中,已经⽣成了BoundSql ,CacheKey 。所以直接调⽤这个⽅法
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) t ErrorContext.instance().Resource()).activity("executing a query").Id());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try { //⼀级缓存(默认是session级别的,和我们的sqlSession对象绑定)的使⽤,从⼀级缓存中查询,不存在数据,则查询数据库
queryStack++;
list = resultHandler == null ? (List<E>) Object(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 (LocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {//这⾥将查询的逻辑交给了具体的⼦类,使⽤的模板⽅法的设计模式。同时我们的执⾏器这块
//也是⼀个典型的包装设计模式。⼆级缓存执⾏器包装下⾯的三种基本执⾏器
//我们查看SimpleExecutor执⾏器的doDuery⽅法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
}
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 Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//如果当前执⾏器有它的包装类,那么wrapper只能是CachingExecutor,否则就是它⾃⼰
//这⾥默认是返回PreparedStatementHandler类型的statement对象
StatementHandler handler = wStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//进⾏了连接的建⽴,statenment对象的创建,参数的赋值操作。(默认PreparedStatementHandler
//其他的statement步骤差不多)
stmt = prepareStatement(handler, ms.getStatementLog());
//进⾏数据库的操作,并对结果进⾏处理为java类型⽐如:ute();
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
我们的StatementHandler的类结构和我们的执⾏器的⼏乎⼀模⼀样。很明显这⼉也是⽤到了装饰设计模式,同时也⽤到了模板⽅法。public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, Bou
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
resultset 遍历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 = Connection();
// 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, Timeout());
handler.parameterize(stmt);
return stmt;
}
//查看代码,由于使⽤的是PooledDataSource,在获取连接对象的时候,会进⾏⼀次代理
protected Connection getConnection(Log statementLog) throws SQLException {
//这⼉的connection 是⼀个代理对象
if (statementLog.isDebugEnabled()) {
//如果是debug,则再次进⾏⼀次代理,好插⼊记录⽇志的功能
wInstance(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 = ClassLoader();
//使⽤jdk的动态代理
return (Connection) wProxyInstance(cl, new Class[]{Connection.class}, handler);
}
@Override
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
if (Object.class.DeclaringClass())) {
return method.invoke(this, params);
}
if ("prepareStatement".Name())) {
//如果是执⾏的 prepareStatement()⽅法
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
//对PrepareStatement进⾏代理
// preparedStatement = connection.prepareStatement(sql); 返回的是⼀个代理对象
stmt = wInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("prepareCall".Name())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = wInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("createStatement".Name())) {
Statement stmt = (Statement) method.invoke(connection, params);
stmt = wInstance(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, Timeout());
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = Sql();
if (KeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = KeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (ResultSetType() == ResultSetType.DEFAULT) {
//默认是执⾏这个逻辑,如果是dubug模式,这⾥返回的statement对象是⼀个代理对象。可以查看上⾯
//新建connnection对象的逻辑,所以⽣产环境不建议⽤debug模式
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, ResultSetType().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);
进⾏查询数据库的操作。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论