目錄
1.1 創建mybatis配置文件
1.2 創建數據庫表
1.3 創建javabean
1.4 創建mapper映射文件
1.5 運行測試
2.3 SqlSession
2.4 Executor
2.5 StatementHandler
2.6 ParameterHandler
2.7 ResultSetHandler
2.8 TypeHandler
三.總結
寫這篇博客,是因為一個面試題“能介紹一下MyBatis執行sql的整個流程嗎?”
之前也看過一下博客,知道大概的流程,無非就是:啟動->解析配置文件->創建executor->綁定參數->執行sql->結果集映射,因為沒有看過源碼,聽別人解釋,自己心里還是有點虛的,畢竟也不知道別人講的是否正確,使用MyBatis也快一年了,所以寫這篇博客總結一下。
原文地址:https://www.cnblogs.com/-beyond/p/13232624.html
一.mybatis極簡示例
1.1 創建mybatis配置文件
文件名隨意,這里命名為為mybatis-config.xml,內容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="cn/ganlixin/mappers/UserMapper.xml"/> </mappers> </configuration>
1.2 創建數據表
在test數據庫中創建user表:
create table user ( id int not null primary key auto_increment, name char(20) not null, age int default 0, addr char(20) default '' ) engine=innodb default charset=utf8mb4; insert into user value (1, 'aaa', 18, '北京');
1.3 創建javabean
創建User類,包含4個字段:
package cn.ganlixin.model; public class User { private Integer id; private String name; private Integer age; private String addr; // 省略getter、setter、toString }
1.4 創建mapper文件
創建UserMapper.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.ganlixin.mappers.UserMapper"> <select id="selectUserById" parameterType="int" resultType="cn.ganlixin.model.User"> select * from user where id=#{id} </select> <delete id="deleteUserById" parameterType="int"> delete from user where id=#{id} </delete> </mapper>
1.5 運行測試
public class TestMyBatis { public static void main(String[] args) throws IOException { String config = "resources/mybatis-config.xml"; InputStream resourceAsStream = Resources.getResourceAsStream(config); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); User user = (User) sqlSession.selectOne("cn.ganlixin.mappers.UserMapper.selectUserById", 1); System.out.println(user); // User{id=1, name='aaa', age=18, addr='北京'} // 下面的delete不會生效,因為mybatis默認沒有開啟自動提交 int delete = sqlSession.delete("cn.ganlixin.mappers.UserMapper.deleteUserById", 1); System.out.println(delete); // 1 } }
到這里,一個簡單的mybatis使用示例就完成了,后面的分析根據上面這個TestMyBatis的執行流程進行分析的。
二.mybatis的幾大“組件”
我這里說的“組件”,可以理解為Mybatis執行過程中的很重要的幾個模塊。
2.1 SqlSessionFactoryBuilder
從名稱長可以看出來使用的建造者設計模式(Builder),用於構建SqlSessionFactory對象
1.解析mybatis的xml配置文件,然后創建Configuration對象(對應<configuration>標簽);
2.根據創建的Configuration對象,創建SqlSessionFactory(默認使用DefaultSqlSessionFactory);
2.2 SqlSessionFactory
從名稱上可以看出使用的工廠模式(Factory),用於創建並初始化SqlSession對象(數據庫會話)
1.調用openSession方法,創建SqlSession對象,可以將SqlSession理解為數據庫連接(會話);
2.openSession方法有多個重載,可以創建SqlSession關閉自動提交、指定ExecutorType、指定數據庫事務隔離級別….
package org.apache.ibatis.session; import java.sql.Connection; public interface SqlSessionFactory { /** * 使用默認配置 * 1.默認不開啟自動提交 * 2.執行器Executor默認使用SIMPLE * 3.使用數據庫默認的事務隔離級別 */ SqlSession openSession(); /** * 指定是否開啟自動提交 * @param autoCommit 是否開啟自動提交 */ SqlSession openSession(boolean autoCommit); /** * 根據已有的數據庫連接創建會話(事務) * @param connection 數據庫連接 */ SqlSession openSession(Connection connection); /** * 創建連接時,指定數據庫事務隔離級別 * @param level 事務隔離界別 */ SqlSession openSession(TransactionIsolationLevel level); /** * 創建連接時,指定執行器類型 * @param execType 執行器 */ SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection); /** * 獲取Configuration對象,也就是解析xml配置文件中的<configuration>標簽后的數據 */ Configuration getConfiguration(); }
2.3 SqlSession
如果了解web開發,就應該知道cookie和session吧,SqlSession的session和web開發中的session概念類似。
session,譯為“會話、會議”,數據的有效時間范圍是在會話期間(會議期間),會話(會議)結束后,數據就清除了。
也可以將SqlSession理解為一個數據庫連接(但也不完全正確,因為創建SqlSession之后,如果不執行sql,那么這個連接是無意義的,所以數據庫連接在執行sql的時候才建立的)。
SqlSession是一個接口,定義了很多操作數據庫的方法聲明:
public interface SqlSession extends Closeable { /* 獲取數據庫連接 */ Connection getConnection(); /* 數據庫的增刪改查 */ <T> T selectOne(String statement); <E> List<E> selectList(String statement); int update(String statement, Object parameter); int delete(String statement, Object parameter); /* 事務回滾與提交 */ void rollback(); void commit(); /* 清除一級緩存 */ void clearCache(); // 此處省略了很多方法 }
SqlSession只是定義了執行sql的一些方法,而具體的實現由子類來完成,比如SqlSession有一個接口實現類DefaultSqlSession。
MyBatis中通過Executor來執行sql的,在創建SqlSession的時候(openSession),同時會創建一個Executor對象,如下:
@Override 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); // 利用傳入的參數,創建executor對象 final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } 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(); } }
2.4 Executor
Executor(人稱“執行器”)是一個接口,定義了對JDBC的封裝;
MyBatis中提供了多種執行器,如下:
上面的圖中,雖然列出了5個Executor(BaseExecutor是抽象類),其實Executor只有三種:
public enum ExecutorType { SIMPLE, // 簡單 REUSE, // 復用 BATCH; // 批量 }
CacheExecutor其實是一個Executor代理類,包含一個delegate,需要創建時手動傳入(要入simple、reuse、batch三者之一);
ClosedExecutor,所有接口都會拋出異常,表示一個已經關閉的Executor;
創建SqlSession時,默認使用的是SimpleExecutor;
下面是創建Executor的代碼:org.apache.ibatis.session.Configuration#newExecutor()
上面說了,Executor是對JDBC的封裝。當我們使用JDBC來執行sql時,一般會先預處理sql,也就是conn.prepareStatement(sql),獲取返回的PreparedStatement對象(實現了Statement接口),再調用statement的executeXxx()來執行sql。
也就是說,Executor在執行sql的時候也是需要創建Statement對象的,下面以SimpleExecutor為例:
2.5 StatementHandler
在JDBC中,是調用Statement.executeXxx()來執行sql;
在MyBatis,也是調用Statement.executeXxx()來執行sql,此時就不得不提StatementHandler,可以將其理解為一個工人,他的工作包括
1.對sql進行預處理;
2.調用statement.executeXxx()執行sql;
3.將數據庫返回的結果集進行對象轉換(ORM);
public interface StatementHandler { /** * 獲取預處理對象 */ Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException; /** * 進行預處理 */ void parameterize(Statement statement) throws SQLException; /** * 批量sql(內部調用statement.addBatch) */ void batch(Statement statement) throws SQLException; /** * 執行更新操作 * @return 修改的記錄數 */ int update(Statement statement) throws SQLException; /** * 執行查詢操作 * @param statement sql生成的statement * @param resultHandler 結果集的處理邏輯 * @return 查詢結果 */ <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; BoundSql getBoundSql(); ParameterHandler getParameterHandler(); }
StatementHandler的相關子類如下圖所示:
以BaseStatementHandler為例:
2.6 ParameterHandler
ParameterHandler的功能就是sql預處理后,進行設置參數:
public interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStatement ps) throws SQLException; }
ParamterHandler有一個DefaultParameterHandler,下面是其重寫setParameters的代碼:
2.7 ResultSetHandler
當執行statement.execute()后,就可以通過statement.getResultSet()來獲取結果集,獲取到結果集之后,MyBatis會使用ResultSetHandler來將結果集的數據轉換為Java對象(ORM映射)。
public interface ResultSetHandler { /** * 從statement中獲取結果集,並將結果集的數據庫屬性字段映射到Java對象屬性 * @param stmt 已經execute的statement,調用state.getResultSet()獲取結果集 * @return 轉換后的數據 */ <E> List<E> handleResultSets(Statement stmt) throws SQLException; <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException; void handleOutputParameters(CallableStatement cs) throws SQLException; }
ResultSetHandler有一個實現類,DefaultResultHandler,其重寫handlerResultSets方法,如下:
2.8 TypeHandler
TypeHandler主要用在兩個地方:
1.參數綁定,發生在ParameterHandler.setParamenters()中。
MyBatis中,可以使用<resultMap>來定義結果的映射關系,包括每一個字段的類型,比如下面這樣:
<resultMap id="baseMap" type="cn.ganlixin.model.User"> <id column="uid" property="id" jdbcType="INTEGER" /> <result column="uname" property="name" jdbcType="VARCHAR" /> </resultMap>
TypeHandler,可以對某個字段按照xml中配置的類型進行設置值,比如設置sql的uid參數時,類型為INTEGER(jdbcType)。
2.獲取結果集中的字段值,發生在ResultSetHandler處理結果集的過程中。
TypeHandler的定義如下:
public interface TypeHandler<T> { /** * 設置預處理參數 * * @param ps 預處理statement * @param i 參數位置 * @param parameter 參數值 * @param jdbcType 參數的jdbc類型 */ void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; /** * 獲取一條結果集的某個字段值 * * @param rs 一條結果集 * @param columnName 列名(字段名稱) * @return 字段值 */ T getResult(ResultSet rs, String columnName) throws SQLException; /** * 獲取一條結果集的某個字段值(按照字段的順序獲取) * * @param rs 一條結果集 * @param columnIndex 字段列的順序 * @return 字段值 */ T getResult(ResultSet rs, int columnIndex) throws SQLException; T getResult(CallableStatement cs, int columnIndex) throws SQLException; }
三.總結
對於上面的一些組件進行介紹后,這里將其串聯起來,那么就能知道mybatis執行sql的具體流程了,於是我花了下面這個流程:
原文地址:https://www.cnblogs.com/-beyond/p/13232624.html