SqlSession是Mybatis最重要的構建之一,可以認為Mybatis一系列的配置目的是生成類似JDBC生成的Connection對象的statement對象,這樣才能與數據庫開啟“溝通”,通過SqlSession可以實現增刪改查(當然現在更加推薦是使用Mapper接口形式)
1 .sqlsession的創建:
SqlSessionFactoryBuilder創建SqlSessionFactory openSession,sqlSession 執行增刪改查
用了注解是通過org.mybatis.spring.SqlSessionFactoryBean該類創建sqlsession的,而mapper里面的每一個方法稱為statement。
public void deleteUserTest() throws IOException { // mybatis配置文件 String resource = "SqlMapConfig.xml"; // 得到配置文件流 InputStream inputStream = Resources.getResourceAsStream(resource); // 創建會話工廠,傳入mybatis的配置文件信息 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(inputStream); // 通過工廠得到SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 傳入id刪除 用戶 sqlSession.delete("test.deleteUser", 39);
//更新
sqlSession.update("test.updateUser", user);
//插入
sqlSession.insert("test.insertUser", user);
//查詢
List<User> list = sqlSession.selectOne("test.findUserByName", "小明"); // 提交事務 增刪改 需要commit,查詢無需commit sqlSession.commit(); // 關閉會話 sqlSession.close(); }
2.SqlSession原理
SqlSession提供select/insert/update/delete方法,在舊版本中使用使用SqlSession接口的這些方法,但是新版的Mybatis中就會建議使用Mapper接口的方法。
映射器其實就是一個動態代理對象,進入到MapperMethod的execute方法就能簡單找到SqlSession的刪除、更新、查詢、選擇方法,從底層實現來說:通過動態代理技術,讓接口跑起來,之后采用命令模式,最后還是采用了SqlSession的接口方法(getMapper()方法等到Mapper)執行SQL查詢(也就是說Mapper接口方法的實現底層還是采用SqlSession接口方法實現的)。
3.SqlSession重要的四個對象
1)Execute:調度執行StatementHandler、ParmmeterHandler、ResultHandler執行相應的SQL語句;
2)StatementHandler:使用數據庫中Statement(PrepareStatement)執行操作,即底層是封裝好了的prepareStatement;
3)ParammeterHandler:處理SQL參數;
4)ResultHandler:結果集ResultSet封裝處理返回。
1 Execute執行器
execute接口有以下方法
執行器起到至關重要的作用,它是真正執行Java與數據庫交互的東西,參與了整個SQL查詢執行過程中。
1)主要有三種執行器:簡易執行器SIMPLE(不配置就是默認執行器)、REUSE是一種重用預處理語句、BATCH批量更新、批量專用處理器
2)執行器作用:Executor會先調用StatementHandler的prepare()方法預編譯SQL語句,同時設置一些基本的運行參數,然后調用StatementHandler的parameterize()方法(實際上是啟用了ParameterHandler設置參數)設置參數,resultHandler再組裝查詢結果返回調用者完成一次查詢完成預編譯,簡單總結起來就是即先預編譯SQL語句,之后設置參數(跟JDBC的prepareStatement過程類似)最后如果有查詢結果就會組裝返回。
StatementHandler 接口方法
public interface StatementHandler { //預編譯SQL語句 Statement prepare(Connection connection) throws SQLException; //設置一些基本的運行參數 void parameterize(Statement statement) throws SQLException; void batch(Statement statement) throws SQLException; int update(Statement statement) throws SQLException; <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; BoundSql getBoundSql(); ParameterHandler getParameterHandler(); }
第一:Executor通過Configuration對象中newExecutor()方法中選擇相應的執行器生成
//Configuration 類
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 = (Executor) interceptorChain.pluginAll(executor); return executor; }
//InterceptorChain 類中pluginAll 方法:
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
第二:在執行器中StatementHandler是根據Configuration構建的
public class SimpleExecutor extends BaseExecutor { public SimpleExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); } public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //獲取StatementHandler 對象 StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//調到最后一個方法查看詳細內容 stmt = prepareStatement(handler, ms.getStatementLog()); return handler.update(stmt); } finally { closeStatement(stmt); } } 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(); StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } } public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException { return Collections.emptyList(); } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection); //設置執行參數 handler.parameterize(stmt); return stmt; } }
第三:Executor會執行StatementHandler的prepare()方法進行預編譯---->填入connection對象等參數---->再調用parameterize()方法設置參數---->完成預編譯
//SimpleExecutor類 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); // 執行handler.prepare()預編譯sql stmt = handler.prepare(connection);
//設置執行參數 handler.parameterize(stmt); return stmt; }
2 StatementHanlder數據庫會話器
1)作用:簡單來說就是專門處理數據庫會話。詳細來說就是進行預編譯並且調用ParameterHandler的setParameters()方法設置參數。
2)數據庫會話器主要有三種:SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分別對應Executor的三種執行器(SIMPLE、REUSE、BATCH)
Executor的prepareStatement()方法中調用了StatementHandler的parameterize()
第一:StatementHandler的生成是由Configuration方法中newStatementHandler()方法生成的,但是正在創建的是實現了StatementHandler接口的RoutingStatementHandler對象
//Configuration
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; }
第二:RoutingStatementHandler的通過適配器模式找到對應(根據上下文)的StatementHandler執行的,並且有SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分別對應Executor的三種執行器(SIMPLE、REUSE、BATCH)
//RoutingStatementHandler 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()); } }
PreparedStatementHandler , public class PreparedStatementHandler extends BaseStatementHandler , BaseStatementHandler implements StatementHandler
第三:在BaseStatementHandler中重寫prepare()方法,instantiateStatement()方法完成預編譯,之后設置一些基礎配置(獲取最大行數,超時)
//BaseStatementHandler 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); } }
第四:instantiateStatement()預編譯實際上也是使用了JDBC的prepareStatement()完成預編譯
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() != null) { return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { return connection.prepareStatement(sql); } }
第五:在prepareStatement中重寫parameterize()方法。prepare()預編譯完成之后,Executor會調用parameterize()方法
public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); }
(3)ParameterHandler參數處理器
作用:對預編譯中參數進行設置,如果有配置typeHandler,自然會對注冊的typeHandler對參數進行處理