簡介
Mybatis是一款流行的持久層框架,基于ORM(Object-Relation Mapper)思想,對針對JDBC的封裝,通過xml配置支持靈活復雜的SQL查詢,
框架組件架構圖
Mybatis核心成員資料流

核心成員說明
| 核心成員 | 功能說明 |
|---|---|
| Configuration | 保存MyBatis大部分配置資訊 |
| SqlSession | MyBatis主要的頂層API,與資料庫互動,實作資料庫增刪改查功能, |
| Executor | MyBatis 調度器,負責SQL陳述句的生成和查詢快取的維護 |
| StatementHandler | 封裝JDBC,負責對JDBC statement 的操作,如設定引數等 |
| ParameterHandler | 用戶傳遞的引數轉換成JDBC Statement 所對應的資料型別 |
| ResultSetHandler | 負責將JDBC回傳的ResultSet結果集物件轉換成List型別的集合 |
| TypeHandler | 負責java資料型別和jdbc資料型別(也可以說是資料表列型別)之間的映射和轉換 |
| MappedStatement | MappedStatement維護一條<select|update|delete|insert>節點的封裝 |
| SqlSource | 負責根據用戶傳遞的parameterObject,動態地生成SQL陳述句,將資訊封裝到BoundSql物件中,并回傳 |
| BoundSql | 表示動態生成的SQL陳述句以及相應的引數資訊 |
核心代碼流程
1)Mybatis通過SqlSessionFactory獲取sqlSession,然后有sqlSession完成資料庫的互動,SqlSessionFactory默認介面實作是是DefaultSqlSessionFactory,
//默認new DefaultSqlSessionFactory()
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
2) SqlSessionFactory有多個openSession方法,以無參的方法為例,
public SqlSession openSession() {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
//executor 默認有多個實作,根據ExecutorType可知有以下幾種
public enum ExecutorType {
SIMPLE,
REUSE,
BATCH;
private ExecutorType() {
}
}
- 針對Dao層的定義的介面,MapperRegistry維護了Dao層介面的代理工廠,并由工廠生成具體的代理去處理sqlSession,并交底層Executor調度器去執行,
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
//MapperMethod對應Dao層介面的方法
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
//根據介面型別生成具體代理
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
- Executor調度器與StatementHandler等處理器互動,完成SQL操作
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
并發場景的思考:DefaultSqlSessionFactory是執行緒安全的么?如何做到執行緒安全?
是不執行緒安全的,這個會在后面解釋,
快取機制

一級快取核心類是PerpetualCache,本質是一個hashMap
二級快取默認不開啟,
Spring 與Mybatis的整合
Spring bean 生命周期
MapperScannerConfigurer
MapperScannerConfigurer的主要作業是掃描basePackage包下所有的mapper介面類,并將mapper介面類封裝成為BeanDefinition物件,注冊到spring的BeanFactory容器中核心類圖如下:
以上知道了Spring的bean注冊到容器的核心流程,通過理解Spring的核心流程,可以梳理出Dao層介面Mapper通過MapperScannerConfigurer整合到spring的流程:

SqlSessionFactoryBean
類定義如下:
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
//省略一些詳細代碼
public void setDataSource(DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();
} else {
this.dataSource = dataSource;
}
}
public void setMapperLocations(Resource[] mapperLocations) {
this.mapperLocations = mapperLocations;
}
}
從這個類的定義可以看出SqlSessionFactoryBean與DataSource和Mapper有關,
在bean被創建的中,通過介面InitializingBean中的afterPropertiesSet方法設定屬性,
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
buildSqlSessionFactory方法主要完成SqlSession的初始化操作,完成在上面講的Mybatis核心代碼流程,
SqlSessionFactoryBean整合進Spring的流程

Mybatis整合Spring的整體流程
以下面簡單的代碼做主要流程說明
@Service
class AService{
@Autowire
private BDao bDao;
}
Spring在初始化的程序中@Service注解的類,AService類初始化完成之后,會進行屬性賦值,bDao介面就是AService的一個屬性,
1)首先根據這個bDao的名字或者型別從spring的BeanFactory中獲取它的BeanDefinition,再從BeanDefinition中獲取BeanClass,bDao對應的BeanClass就是MapperFactoryBean,這在創建MapperScannerConfigurer物件的時候設定的,
2)創建MapperFactoryBean物件,創建完成后,對屬性進行賦值,其中有一個屬性就是SqlSessionFactoryBean
3)MapperFactoryBean物件的屬性設定完成之后,就呼叫它的getObject()方法,來獲取bDao對應的實作類,獲取的是一個JDK的代理類
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
}
程式在呼叫AService物件的某個方法的時候,就會呼叫到MapperProxy物件的invoke()方法,去完成對資料庫的操作,
如何解決SqlSession的執行緒安全問題
MapperFactoryBean.getObject()獲取的實體,實際是通過一個SqlSessionTemplate物件創建的,注入的Mapper物件實際上最終都執行的是SqlSessionTemplate方法,
關鍵代碼如下:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
Assert.notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/289261.html
標籤:java
