天天看點

mybatis源碼探索之SqlSessionSqlSessionFactorySqlSession

文章目錄

  • SqlSessionFactory
    • 方法
    • DefaultSqlSessionFactory
  • SqlSession
    • 方法
    • 舉個栗子

SqlSessionFactory

SqlSessionFactory 使用了工廠模式建立SqlSession的,它的預設實作類是DefaultSqlSessionFactory。

方法

它共有以下幾個方法:

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,具體實作可以參考DefaultSqlSessionFactory源碼

DefaultSqlSessionFactory

它的核心方法是openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit),從資料源中擷取SqlSession。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      //擷取事務工廠,預設是ManagedTransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 擷取執行器,預設是SimpleExecutor
      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();
    }
  }
           

常用的方法是openSession(),但是調用的還是核心方法。通過無參的openSession()方法建立SqlSession時,事務預設不自動送出,事務隔離級别null,預設執行器類型是ExecutorType.REUSE

@Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
           

SqlSession

SqlSession 是 MyBaits 對外提供的最關鍵的核心接口,通過它可以執行資料庫讀寫指令、擷取映射器、管理事務等; SqlSession 也意味着用戶端與資料庫的一次連接配接,用戶端對資料庫的通路請求都是由SqlSession來處理的,SqlSession 由 SqlSessionFactory 建立,每個 SqlSession 都會引用 SqlSessionFactory 中全局唯一單例存在的 configuration 對象。

SqlSession的預設實作類是DefaultSqlSession

方法

SqlSession的方法主要分為以下6類:

1、SQL語句執行方法

2、立即批量更新方法

3、事務控制方法

4、本地緩存

5、映射器擷取

6、SqlSession關閉

對應的主要方法如下:

SqlSession的方法中參數statement,都是**Mapper接口中的方法的全限定名,例如:mapperClasses.UserInfoMapper.selectByPrimaryKey。

1、SQL語句執行方法

int delete(String statement, Object parameter);
int insert(String statement, Object parameter);
int update(String statement, Object parameter);

void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
<T> T selectOne(String statement, Object parameter);
 <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
 <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
 <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
           

delete、insert方法最終調用的還是update方法,update源碼如下:

public int update(String statement, Object parameter) {
    try {
      dirty = true;
      // 擷取MappedStatement 
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

           

select、selectOne最終調用的是selectList方法,selectMap方法也是通過selectList方法查詢到結果的,隻不過再查詢之後又轉換成了map對象。

selectList方法源碼如下:

private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
           

selectOne源碼:查詢結果集如果是一個就取這個,多個則抛異常

public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }
           

selectCursor提供了與List相同的結果,隻是它使用疊代器惰性地擷取資料。

selectCursor 應該很少使用(應該吧,至少我沒有用過)

public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
      registerCursor(cursor);
      return cursor;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
           

2、立即批量更新方法

隻有這個方法:

可以使用這個方法清除(執行)緩存在 JDBC 驅動類中的批量更新語句。

List flushStatements();

public List<BatchResult> flushStatements() {
    try {
      return executor.flushStatements();
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error flushing statements.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
           

3、事務控制方法

//重新整理緩存在 JDBC 驅動類中的批量語句并送出資料庫連接配接,如果資料庫連接配接沒有updates/deletes/inserts被調用則不會送出
void commit();
//commit()更新版,可以根據參數來決定是否強制送出
void commit(boolean force);

//丢棄挂起的批處理語句并復原資料庫連接配接,如果資料庫連接配接沒有updates/deletes/inserts被調用則不會復原
void rollback();
//rollback()的更新版,可以根據參數來決定是否強制復原
void rollback(boolean force);
           

4、本地緩存

//清空本地緩存,隻是清空本地緩存,跟二級緩存沒關系
void clearCache();
           

5、映射器擷取

//從configuration對象中的mapperRegistry(mapper系統資料庫)中擷取對應的*mapper對象
public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }
           

6、SqlSession關閉方法

//關閉session會話
void close();
           

舉個栗子

public class UserTest {

  private final static SqlSessionFactory sqlSessionFactory;
  static {
    String resource = "mybatis-config.xml";
    Reader reader = null;
    try {
      reader = Resources.getResourceAsReader(resource);
    } catch (IOException e) {
      System.out.println(e.getMessage());
    }
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }
  @Test
  public void test(){
    SqlSession sqlSession = sqlSessionFactory.openSession();

    UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
    UserInfo userInfos = sqlSession.selectOne("mapperClasses.UserInfoMapper.selectByPrimaryKey" , 1L);
    System.out.println(userInfos);
    UserInfo userInfo = mapper.selectByPrimaryKey(4L);
    System.out.println(userInfo);
  }
}
           

使用mybatis進行crud有兩種方式:

1、通過sqlSession提供的操作進行crud,這種是最原始的使用方式。這種方式可讀性、可維護性都很差,一般很少使用。

2、通過mapper進行crud,最終調用的還是第一種的方式。這種方式是ibatis被谷歌收購後,谷歌進行更新的

UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
UserInfo userInfo = mapper.selectByPrimaryKey(4L);
           

能力有限,水準一般,如有錯誤,請多指出。

繼續閱讀