MyBatis簡單介紹
MyBatis是一個持久層框架,使用簡單,學習成本較低,可以執行自己手寫的SQL陳述句,比較靈活,但是MyBatis的自動化程度不高,移植性也不高,有時從一個資料庫遷移到另外一個資料庫的時候需要自己修改配置,
一個Mybatis最簡單的使用列子如下:
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import java.io.InputStream;
import java.util.List;
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception{
ClassPathResource resource = new ClassPathResource("mybatis-config.xml");
InputStream inputStream = resource.getInputStream();
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void selectUserTest(){
String id = "123456";
SqlSession sqlSession = sqlSessionFactory.openSession();
CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class);
Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id);
System.out.println(cbondissuer);
sqlSession.close();
}
}
總結下就是分為下面四個步驟:
- 從組態檔(通常是XML檔案)得到SessionFactory;
- 從SessionFactory得到SqlSession;
- 通過SqlSession進行CRUD和事務的操作;
- 執行完相關操作之后關閉Session,
啟動流程分析
本博客只涉及創建SessionFactory,以及從SessionFactory獲取SqlSession的流程,具體執行Sql的流程會在其他博客中分析,
ClassPathResource resource = new ClassPathResource("mybatis-config.xml");
InputStream inputStream = resource.getInputStream();
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
通過上面代碼發現,創建SqlSessionFactory的代碼在SqlSessionFactoryBuilder中,進去一探究竟:
//整個程序就是將組態檔決議成Configration物件,然后創建SqlSessionFactory的程序
//Configuration是SqlSessionFactory的一個內部屬性
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
下面我們看下決議組態檔程序中的一些細節,
先給出一個組態檔的列子:
<?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>
<!--SqlSessionFactoryBuilder中配置的組態檔的優先級最高;config.properties組態檔的優先級次之;properties標簽中的配置優先級最低 -->
<properties resource="org/mybatis/example/config.properties">
<property name="username" value=https://www.cnblogs.com/54chensongxia/p/"dev_user"/>
下面是決議組態檔的核心方法:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//決議properties標簽,并set到Configration物件中
//在properties配置屬性后,在Mybatis的組態檔中就可以使用${key}的形式使用了,
propertiesElement(root.evalNode("properties"));
//決議setting標簽的配置
Properties settings = settingsAsProperties(root.evalNode("settings"));
//添加vfs的自定義實作,這個功能不怎么用
loadCustomVfs(settings);
//配置類的別名,配置后就可以用別名來替代全限定名
//mybatis默認設定了很多別名,參考附錄部分
typeAliasesElement(root.evalNode("typeAliases"));
//決議攔截器和攔截器的屬性,set到Configration的interceptorChain中
//MyBatis 允許你在已映射陳述句執行程序中的某一點進行攔截呼叫,默認情況下,MyBatis 允許使用插件來攔截的方法呼叫包括:
//Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
//ParameterHandler (getParameterObject, setParameters)
//ResultSetHandler (handleResultSets, handleOutputParameters)
//StatementHandler (prepare, parameterize, batch, update, query)
pluginElement(root.evalNode("plugins"));
//Mybatis創建物件是會使用objectFactory來創建物件,一般情況下不會自己配置這個objectFactory,使用系統默認的objectFactory就好了
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//設定在setting標簽中配置的配置
settingsElement(settings);
//決議環境資訊,包括事物管理器和資料源,SqlSessionFactoryBuilder在決議時需要指定環境id,如果不指定的話,會選擇默認的環境;
//最后將這些資訊set到Configration的Environment屬性里面
environmentsElement(root.evalNode("environments"));
//
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//無論是 MyBatis 在預處理陳述句(PreparedStatement)中設定一個引數時,還是從結果集中取出一個值時, 都會用型別處理器將獲取的值以合適的方式轉換成 Java 型別,決議typeHandler,
typeHandlerElement(root.evalNode("typeHandlers"));
//決議Mapper
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

上面決議流程結束后會生成一個Configration物件,包含所有配置資訊,然后會創建一個SqlSessionFactory物件,這個物件包含了Configration物件,
下面是openSession的程序:
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);
//根據獲取的執行器創建SqlSession
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();
}
}
//interceptorChain生成代理類,具體參見Plugin這個類的方法
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
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);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
Executor分成兩大類,一類是CacheExecutor,另一類是普通Executor,
普通Executor又分為三種基本的Executor執行器,SimpleExecutor、ReuseExecutor、BatchExecutor,
- SimpleExecutor:每執行一次update或select,就開啟一個Statement物件,用完立刻關閉Statement物件,
- ReuseExecutor:執行update或select,以sql作為key查找Statement物件,存在就使用,不存在就創建,用完后,不關閉Statement物件,而是放置于Map<String, Statement>內,供下一次使用,簡言之,就是重復使用Statement物件,
- BatchExecutor:執行update(沒有select,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它快取了多個Statement物件,每個Statement物件都是addBatch()完畢后,等待逐一執行executeBatch()批處理,與JDBC批處理相同,
作用范圍:Executor的這些特點,都嚴格限制在SqlSession生命周期范圍內,
CacheExecutor其實是封裝了普通的Executor,和普通的區別是在查詢前先會查詢快取中是否存在結果,如果存在就使用快取中的結果,如果不存在還是使用普通的Executor進行查詢,再將查詢出來的結果存入快取,

到此為止,我們已經獲得了SqlSession,拿到SqlSession就可以執行各種CRUD方法了,
簡單總結
對于MyBatis啟動的流程(獲取SqlSession的程序)這邊簡單總結下:
- SqlSessionFactoryBuilder決議組態檔,包括屬性配置、別名配置、攔截器配置、環境(資料源和事務管理器)、Mapper配置等;決議完這些配置后會生成一個Configration物件,這個物件中包含了MyBatis需要的所有配置,然后會用這個Configration物件創建一個SqlSessionFactory物件,這個物件中包含了Configration物件;
- 拿到SqlSessionFactory物件后,會呼叫SqlSessionFactory的openSesison方法,這個方法會創建一個Sql執行器(Executor),這個Sql執行器會代理你配置的攔截器方法,
- 獲得上面的Sql執行器后,會創建一個SqlSession(默認使用DefaultSqlSession),這個SqlSession中也包含了Configration物件,所以通過SqlSession也能拿到全域配置;
- 獲得SqlSession物件后就能執行各種CRUD方法了,
SQL的具體執行流程見后續博客,
一些重要類總結:
- SqlSessionFactory
- SqlSessionFactoryBuilder
- SqlSession(默認使用DefaultSqlSession)
- Executor介面
- Plugin、InterceptorChain的pluginAll方法
參考
- 《深入理解mybatis原理》 MyBatis的架構設計以及實體分析
- 流程圖總結
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/2505.html
標籤:其它
下一篇:常用JDBC資料庫驅動包和類名
