天天看點

MyBatis中的設計模式

本篇聊一聊

MyBatis

所用到的一些設計模式。

關于MyBatis一些實作原理,可以參考 MyBatis的Mapper機制

1.工場模式

關于工場模式的具體原理和實作可以參考 淺談模式 - 工場模式

SqlSessionFactory

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();
}
           

上面這部分沒太多好解釋的。

純粹從設計模式角度講,就是工場方法模式,建構單例次元的

SqlSession

對象。

從MyBatis本身講,用來打開會話,如果使用了

mybatis-spring

,這部分是被屏蔽了的。參考文檔 MyBatis-Spring的Mapper機制

MapperProxyFactory

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}
           

實際使用了

JDK

的動态代理,在調用

newInstance

方法的時候會建立一個動态代理對象

Mapper

對象。具體原理的講解可以參考 MyBatis的Mapper機制

2.建造者模式

Environment.Builder

SqlSessionFactoryBuilder

建造者模式,原理和結構都比較簡單,列出了MyBatis的實用點,具體的代碼比較大,就不列了,自己檢視即可。基本原理可以參考文檔 淺談模式 - 建造者模式

3.代理模式

關于工場模式的具體原理和實作可以參考 淺談模式 - 代理模式

非Spring的情況

在調用

sqlSession.getMapper

的時候,實時生成動态代理類(生命周期是這一次調用或者一個調用會話)。

invoke

完成之後,調用

sqlsession.close()

,看你有沒有配置連接配接池,如果配置了

connection

應該是被代理或者包裝過了。内部實際不是真正的斷開連接配接,而是放回池中。參考文檔 MyBatis的Mapper機制

Mybatis-Spring的情況

加上

spring

之後,又在

Mapper

代理的基礎上,做了

SqlSession

的代理。關鍵類

SqlSessionTemplate

。參考文檔 MyBatis-Spring的Mapper機制

4.裝飾器模式

SqlSessionTemplate

SqlSession

的包裝。屬于裝飾器模式。

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  	// 包裝了SqlSession的代理對象
    private final SqlSession sqlSessionProxy;
  	
  	// 省去了大量代碼
    
  	// 動态代理類
    private class SqlSessionInterceptor implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = getSqlSession(
                    SqlSessionTemplate.this.sqlSessionFactory,
                    SqlSessionTemplate.this.executorType,
                    SqlSessionTemplate.this.exceptionTranslator);
            try {
                Object result = method.invoke(sqlSession, args);
                if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    // force commit even on non-dirty sessions because some databases require
                    // a commit/rollback before calling close()
                    sqlSession.commit(true);
                }
                return result;
            } catch (Throwable t) {
                Throwable unwrapped = unwrapThrowable(t);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
                    closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }
                throw unwrapped;
            } finally {
                if (sqlSession != null) {
                    closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }
            }
        }
    }
}
           

5.模闆方法模式

BaseExecutor

。這是個抽象類,一般抽象類中看到下面這種抽象方法,就是用了模闆方法模式

protected abstract int doUpdate(MappedStatement ms, Object parameter)
    throws SQLException;

protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
    throws SQLException;

protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
    throws SQLException;

protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
    throws SQLException;
           

其他還有一些非核心的設計模式運用。比如:

PropertyTokenizer

,運用了疊代器模式。

ErrorContext

,運用了線程級别的單例模式。