魯春利的工作筆記,好記性不如爛筆頭
創(chuàng)新互聯(lián)長期為上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為平川企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作,平川網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
轉(zhuǎn)載自:深入淺出MyBatis-Sqlsession
SqlSession的創(chuàng)建
Sqlsession對應(yīng)著一次數(shù)據(jù)庫會話。由于數(shù)據(jù)庫回話不是永久的,因此Sqlsession的生命周期也不應(yīng)該是永久的,相反,在你每次訪問數(shù)據(jù)庫時(shí)都需要?jiǎng)?chuàng)建它(當(dāng)然并不是說在Sqlsession里只能執(zhí)行一次sql,你可以執(zhí)行多次,當(dāng)一旦關(guān)閉了Sqlsession就需要重新創(chuàng)建它)。創(chuàng)建Sqlsession的地方只有一個(gè),那就是SqlsessionFactory的openSession方法:
package org.apache.ibatis.session; import java.sql.Connection; public interface SqlSessionFactory { SqlSession openSession(); SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection); Configuration getConfiguration(); }
實(shí)際創(chuàng)建SqlSession的地方是openSessionFromDataSource,如下:
package org.apache.ibatis.session.defaults; import java.sql.Connection; import java.sql.SQLException; import org.apache.ibatis.exceptions.ExceptionFactory; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.TransactionIsolationLevel; import org.apache.ibatis.transaction.Transaction; import org.apache.ibatis.transaction.TransactionFactory; import org.apache.ibatis.transaction.managed.ManagedTransactionFactory; public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } 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, autoCommit); return new DefaultSqlSession(configuration, executor); } 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(); } } }
可以看出,創(chuàng)建sqlsession經(jīng)過了以下幾個(gè)主要步驟:
1) 從配置中獲取Environment;
2) 從Environment中取得DataSource;
3) 從Environment中取得TransactionFactory;
4) 從DataSource里獲取數(shù)據(jù)庫連接對象Connection;
5) 在取得的數(shù)據(jù)庫連接上創(chuàng)建事務(wù)對象Transaction;
6) 創(chuàng)建Executor對象(該對象非常重要,事實(shí)上sqlsession的所有操作都是通過它完成的);
7) 創(chuàng)建sqlsession對象。
Sqlsession只是個(gè)門面,真正干事的是Executor,Sqlsession對數(shù)據(jù)庫的操作都是通過Executor來完成的。與Sqlsession一樣,Executor也是動(dòng)態(tài)創(chuàng)建的:
final Executor executor = configuration.newExecutor(tx, execType, autoCommit);
Configuration類的實(shí)現(xiàn):
public class Configuration { protected Environment environment; public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; } }
Executor的定義:
package org.apache.ibatis.executor; import java.sql.SQLException; import java.util.List; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.transaction.Transaction; public interface Executor { ResultHandler NO_RESULT_HANDLER = null; int update(MappedStatement ms, Object parameter) throws SQLException;List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; List flushStatements() throws SQLException; void commit(boolean required) throws SQLException; void rollback(boolean required) throws SQLException; CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); boolean isCached(MappedStatement ms, CacheKey key); void clearLocalCache(); void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class> targetType); Transaction getTransaction(); void close(boolean forceRollback); boolean isClosed(); }
可以看出,創(chuàng)建的Executor只是3中基礎(chǔ)類型之一:
BatchExecutor專門用于執(zhí)行批量sql操作
ReuseExecutor會重用statement執(zhí)行sql操作
SimpleExecutor只是簡單執(zhí)行sql沒有什么特別的。
如果開啟cache的話(默認(rèn)是開啟的并且沒有任何理由去關(guān)閉它),就會創(chuàng)建CachingExecutor,它以前面創(chuàng)建的Executor作為唯一參數(shù)。CachingExecutor在查詢數(shù)據(jù)庫前先查找緩存,若沒找到的話調(diào)用delegate(就是構(gòu)造時(shí)傳入的Executor對象)從數(shù)據(jù)庫查詢,并將查詢結(jié)果存入緩存中。
package org.apache.ibatis.executor; import java.sql.SQLException; import java.util.List; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.cache.TransactionalCacheManager; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.mapping.StatementType; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.transaction.Transaction; public class CachingExecutor implements Executor { private Executor delegate; private boolean autoCommit; // issue #573. No need to call commit() on autoCommit sessions private TransactionalCacheManager tcm = new TransactionalCacheManager(); private boolean dirty; public CachingExecutor(Executor delegate) { this(delegate, false); } public CachingExecutor(Executor delegate, boolean autoCommit) { this.delegate = delegate; this.autoCommit = autoCommit; } public Transaction getTransaction() { return delegate.getTransaction(); } public void close(boolean forceRollback) { try { //issue #499. Unresolved session handling //issue #573. Autocommit sessions should commit if (dirty && !autoCommit) { tcm.rollback(); } else { tcm.commit(); } } finally { delegate.close(forceRollback); } } // 其他代碼略 }
Executor對象是可以被插件攔截的(攔截器Interceptor,如:
@Intercepts({@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
),如果定義了針對Executor類型的插件,最終生成的Executor對象是被各個(gè)插件插入后的代理對象。
Mybatis官方手冊建議通過mapper對象訪問mybatis,因?yàn)槭褂胢apper看起來更優(yōu)雅,就像下面這樣:
session = sqlSessionFactory.openSession(); UserMapper userMapper= session.getMapper(UserMapper.class); UserBo user = new UserBo(); user.setUsername("iMbatis"); user.setPassword("iMbatis"); userMapper.insertUser(user);
那么這個(gè)mapper到底是什么呢,它是如何創(chuàng)建的呢,它又是怎么與sqlsession等關(guān)聯(lián)起來的呢?下面為你一一解答。
表面上看mapper是在sqlsession里創(chuàng)建的,但實(shí)際創(chuàng)建它的地方是MapperRegistry:
package org.apache.ibatis.session; import java.io.Closeable; import java.sql.Connection; import java.util.List; import java.util.Map; import org.apache.ibatis.executor.BatchResult; /** * The primary Java interface for working with MyBatis. * Through this interface you can execute commands, get mappers and manage transactions. * */ public interface SqlSession extends Closeable {T selectOne(String statement); T selectOne(String statement, Object parameter); List selectList(String statement); List selectList(String statement, Object parameter); List selectList(String statement, Object parameter, RowBounds rowBounds); Map selectMap(String statement, String mapKey); Map selectMap(String statement, Object parameter, String mapKey); Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds); void select(String statement, Object parameter, ResultHandler handler); void select(String statement, ResultHandler handler); void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler); int insert(String statement); int insert(String statement, Object parameter); int update(String statement); int update(String statement, Object parameter); int delete(String statement); int delete(String statement, Object parameter); void commit(); void commit(boolean force); void rollback(); void rollback(boolean force); List flushStatements(); void close(); void clearCache(); Configuration getConfiguration(); T getMapper(Class type); Connection getConnection(); }
SqlSeesion是接口,getMapper肯定是通過起實(shí)現(xiàn)類來完成的,查看其類結(jié)構(gòu):
package org.apache.ibatis.session.defaults; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; public class DefaultSqlSession implements SqlSession { private Configuration configuration; private Executor executor; private boolean dirty; publicT getMapper(Class type) { return configuration. getMapper(type, this); } } -- 其中configuration的實(shí)現(xiàn)為 public T getMapper(Class type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } -- MapperRegistry的實(shí)現(xiàn)為 package org.apache.ibatis.binding; public class MapperRegistry { private Configuration config; private final Map , MapperProxyFactory>> knownMappers = new HashMap , MapperProxyFactory>>(); public MapperRegistry(Configuration config) { this.config = config; } @SuppressWarnings("unchecked") public T getMapper(Class type, SqlSession sqlSession) { final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory ) knownMappers.get(type); if (mapperProxyFactory == null) throw new BindingException("Type " + type + " is not known to the MapperRegistry."); try { // ------ return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } public boolean hasMapper(Class type) { return knownMappers.containsKey(type); } public void addMapper(Class type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory (type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
可以看到,mapper是一個(gè)代理對象,它實(shí)現(xiàn)的接口就是傳入的type,這就是為什么mapper對象可以通過接口直接訪問。同時(shí)還可以看到,創(chuàng)建mapper代理對象時(shí)傳入了sqlsession對象,這樣就把sqlsession也關(guān)聯(lián)起來了。我們進(jìn)一步看看mapperProxyFactory.newInstance(sqlSession);背后發(fā)生了什么事情:
package org.apache.ibatis.binding; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.ibatis.session.SqlSession; public class MapperProxyFactory{ private final Class mapperInterface; private Map methodCache = new ConcurrentHashMap (); public MapperProxyFactory(Class mapperInterface) { this.mapperInterface = mapperInterface; } public Class getMapperInterface() { return mapperInterface; } public Map getMethodCache() { return methodCache; } /** * newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) * ClassLoader loader :類加載器,一般使用和被代理對象相同的類加載器 * Class>[] interfaces :被代理對象(目標(biāo)對象)的接口數(shù)組 * InvocationHandler h : 設(shè)置回調(diào)對象,當(dāng)代理對象的方法被調(diào)用時(shí),會委派給該參數(shù)指定對象的invoke方法 */ @SuppressWarnings("unchecked") protected T newInstance(MapperProxy mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy mapperProxy = new MapperProxy (sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
看起來沒什么特別的,和其他代理類的創(chuàng)建一樣,我們重點(diǎn)關(guān)注一下MapperProxy的invoke方法。
我們知道對被代理對象的方法的訪問都會落實(shí)到代理者的invoke上來,MapperProxy的invoke如下:
package org.apache.ibatis.binding; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Map; import org.apache.ibatis.reflection.ExceptionUtil; import org.apache.ibatis.session.SqlSession; /** * @author Clinton Begin * @author Eduardo Macarron */ public class MapperProxyimplements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class mapperInterface; private final Map methodCache; public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { // 轉(zhuǎn)調(diào)具體目標(biāo)對象的方法,這里目標(biāo)對象為this return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } }
可以看到invoke把執(zhí)行權(quán)轉(zhuǎn)交給了MapperMethod,我們來看看MapperMethod里又是怎么運(yùn)作的:
package org.apache.ibatis.binding; import org.apache.ibatis.annotations.Flush; import org.apache.ibatis.annotations.MapKey; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.*; /** * @author Clinton Begin * @author Eduardo Macarron * @author Lasse Voss */ public class MapperMethod { private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, method); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { 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 { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { 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; } }
可以看到,MapperMethod就像是一個(gè)分發(fā)者,他根據(jù)參數(shù)和返回值類型選擇不同的sqlsession方法來執(zhí)行。這樣mapper對象與sqlsession就真正的關(guān)聯(lián)起來了。
sqlsession只是一個(gè)門面,真正發(fā)揮作用的是executor,對sqlsession方法的訪問最終都會落到executor的相應(yīng)方法上去。
Executor分成兩大類:一類是CacheExecutor,另一類是普通Executor。
CacheExecutor有一個(gè)重要屬性delegate,它保存的是某類普通的Executor,值在構(gòu)照時(shí)傳入。執(zhí)行數(shù)據(jù)庫update操作時(shí),它直接調(diào)用delegate的update方法,執(zhí)行query方法時(shí)先嘗試從cache中取值,取不到再調(diào)用delegate的查詢方法,并將查詢結(jié)果存入cache中。
@Override publicList query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, parameterObject, boundSql); @SuppressWarnings("unchecked") List list = (List ) tcm.getObject(cache, key); if (list == null) { list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } // delegate.query實(shí)際是org.apache.ibatis.executor.BaseExecutor.query // 最終會調(diào)用BaseExecutor return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
普通Executor有3類,他們都繼承于BaseExecutor,BatchExecutor專門用于執(zhí)行批量sql操作,ReuseExecutor會重用statement執(zhí)行sql操作,SimpleExecutor只是簡單執(zhí)行sql沒有什么特別的。下面以SimpleExecutor為例:
@Override publicList doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler. query(stmt, resultHandler); } finally { closeStatement(stmt); } }
可以看出,Executor本質(zhì)上也是個(gè)甩手掌柜,具體的事情原來是StatementHandler來完成的。
當(dāng)Executor將指揮棒交給StatementHandler后,接下來的工作就是StatementHandler的事了。我們先看看StatementHandler是如何創(chuàng)建的。
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
configuration.newStatementHandler的實(shí)現(xiàn)為:
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; }
可以看到每次創(chuàng)建的StatementHandler都是RoutingStatementHandler,它只是一個(gè)分發(fā)者,他一個(gè)屬性delegate用于指定用哪種具體的StatementHandler。
同時(shí)還要注意到StatementHandler是可以被攔截器攔截的,和Executor一樣,被攔截器攔截后的對像是一個(gè)代理對象。由于mybatis沒有實(shí)現(xiàn)數(shù)據(jù)庫的物理分頁,眾多物理分頁的實(shí)現(xiàn)都是在這個(gè)地方使用攔截器實(shí)現(xiàn)的。
可選的StatementHandler有SimpleStatementHandler、PreparedStatementHandler和CallableStatementHandler三種。選用哪種在mapper配置文件的每個(gè)statement里指定,默認(rèn)的是PreparedStatementHandler。
statementType的取值是通過DTD文件來約束的:
詳見:http://mybatis.org/dtd/mybatis-3-mapper.dtd
StatementHandler創(chuàng)建后需要執(zhí)行一些初始操作,比如statement的開啟和參數(shù)設(shè)置、對于PreparedStatement還需要執(zhí)行參數(shù)的設(shè)置操作等。
代碼如下:org.apache.ibatis.executor.SimpleExecutor
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection); handler.parameterize(stmt); return stmt; }
statement的開啟和參數(shù)設(shè)置沒什么特別的地方:
package org.apache.ibatis.executor.statement; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.ExecutorException; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; /** * @author Clinton Begin */ public class RoutingStatementHandler implements StatementHandler { private final StatementHandler delegate; 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()); } } @Override public Statement prepare(Connection connection) throws SQLException { return delegate.prepare(connection); } }
org.apache.ibatis.executor.statement.PreparedStatementHandler并沒有對prepare的具體實(shí)現(xiàn),調(diào)用的還是其父類org.apache.ibatis.executor.statement.BaseStatementHandler的實(shí)現(xiàn):
@Override public Statement prepare(Connection connection) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { statement = instantiateStatement(connection); setStatementTimeout(statement); setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } }
handler.parameterize倒是可以看看是怎么回事:
handler.parameterize通過調(diào)用ParameterHandler的setParameters完成參數(shù)的設(shè)置,ParameterHandler隨著StatementHandler的創(chuàng)建而創(chuàng)建,默認(rèn)的實(shí)現(xiàn)是DefaultParameterHandler:
// ====================================================================== package org.apache.ibatis.executor.statement; /** * @author Clinton Begin */ public class PreparedStatementHandler extends BaseStatementHandler { public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); } // 代碼略 @Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } } // ====================================================================== package org.apache.ibatis.executor.statement; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.ExecutorException; import org.apache.ibatis.executor.keygen.KeyGenerator; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.reflection.factory.ObjectFactory; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.type.TypeHandlerRegistry; /** * @author Clinton Begin */ public abstract class BaseStatementHandler implements StatementHandler { protected final Configuration configuration; protected final ObjectFactory objectFactory; protected final TypeHandlerRegistry typeHandlerRegistry; protected final ResultSetHandler resultSetHandler; protected final ParameterHandler parameterHandler; protected final Executor executor; protected final MappedStatement mappedStatement; protected final RowBounds rowBounds; protected BoundSql boundSql; 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); boundSql = mappedStatement.getBoundSql(parameterObject); } this.boundSql = boundSql; // 默認(rèn)為org.apache.ibatis.scripting.defaults.DefaultParameterHandler this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); } }
同Executor和StatementHandler一樣,ParameterHandler也是可以被攔截的。
DefaultParameterHandler
DefaultParameterHandler里設(shè)置參數(shù)的代碼如下:
package org.apache.ibatis.scripting.defaults; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import org.apache.ibatis.executor.ErrorContext; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeException; import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; /** * @author Clinton Begin * @author Eduardo Macarron */ public class DefaultParameterHandler implements ParameterHandler { private final TypeHandlerRegistry typeHandlerRegistry; private final MappedStatement mappedStatement; private final Object parameterObject; private BoundSql boundSql; private Configuration configuration; public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { this.mappedStatement = mappedStatement; this.configuration = mappedStatement.getConfiguration(); this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry(); this.parameterObject = parameterObject; this.boundSql = boundSql; } @Override public Object getParameterObject() { return parameterObject; } @Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); ListparameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } catch (SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } } }
這里面最重要的一句其實(shí)就是最后一句代碼,它的作用是用合適的TypeHandler完成參數(shù)的設(shè)置。那么什么是合適的TypeHandler呢,它又是如何決斷出來的呢?BaseStatementHandler的構(gòu)造方法里有這么一句:
if (boundSql == null) { // issue #435, get the key before calculating the statement generateKeys(parameterObject); boundSql = mappedStatement.getBoundSql(parameterObject); }
它觸發(fā)了sql 的解析,在解析sql的過程中,TypeHandler也被決斷出來了,決斷的原則就是根據(jù)參數(shù)的類型和參數(shù)對應(yīng)的JDBC類型決定使用哪個(gè)TypeHandler。比如:參數(shù)類型是String的話就用StringTypeHandler,參數(shù)類型是整數(shù)的話就用IntegerTypeHandler等。
參數(shù)設(shè)置完畢后,執(zhí)行數(shù)據(jù)庫操作(update或query)。如果是query最后還有個(gè)查詢結(jié)果的處理過程。
ResultSetHandler
結(jié)果處理使用ResultSetHandler來完成,默認(rèn)的ResultSetHandler是FastResultSetHandler,它在創(chuàng)建StatementHandler時(shí)一起創(chuàng)建,代碼如下:
org.apache.ibatis.executor.statement.BaseStatementHandler
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
org.apache.ibatis.session.Configuration
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; }
org.apache.ibatis.executor.statement.PreparedStatementHandler
@Override publicList query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler. handleResultSets(ps); }
可以看出ResultSetHandler也是可以被攔截的,可以編寫自己的攔截器改變ResultSetHandler的默認(rèn)行為。
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { ListautoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; if (autoMapping.size() > 0) { for (UnMappedColumAutoMapping mapping : autoMapping) { final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); // issue #377, call setter on nulls if (value != null || configuration.isCallSettersOnNulls()) { if (value != null || !mapping.primitive) { metaObject.setValue(mapping.property, value); } foundValues = true; } } } return foundValues; }
從代碼里可以看到,決斷TypeHandler使用的是結(jié)果參數(shù)的屬性類型。因此我們在定義作為結(jié)果的對象的屬性時(shí)一定要考慮與數(shù)據(jù)庫字段類型的兼容性。