MyBatis-05-MyBatis使用流程簡讀

YangDanMua發表於2024-04-16

程式碼

// 使用流程
// 1. 讀取配置檔案
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2. 建立 SqlSessionFactory 工廠
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 生產 SqlSession 物件
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 獲取 代理物件
UserDao userDao = sqlSession.getMapper(UserDao.class);
// 5. 執行方法
List<User> users = userDao.findAll();
System.out.println(Arrays.toString(users.toArray()));
// 6. 釋放資源
sqlSession.close();
inputStream.close();

new SqlSessionFactoryBuilder().build

new SqlSessionFactoryBuilder: 沒有做額外動作,只是新建了一個例項,說明它不是執行緒安全的,每次使用要新建一個
build: 解析 XML,並將生成的相關配置儲存到 Configuration 例項中,並且使用這個 Configuration 作為建構函式引數構建 DefaultSqlSessionFactory 例項

DefaultSqlSessionFactory

SqlSessionFactory: SqlSession 的例項工廠

  1. 獲取 MyBatis 配置物件 Configuration
  2. 可配置
  • 自動提交
  • 連線物件
  • 事務隔離級別
  • 執行器型別

兩個實現類: DefaultSqlSessionFactory、SqlSessionManager

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

DefaultSqlSessionFactory

  1. 不傳是否自動提交,預設不自動提交
  2. 不傳事務隔離級別,預設使用資料庫的事務隔離級別
  3. 不傳執行器型別,預設使用 Configuration 的配置(預設是 SIMPLE)
  4. 儲存了 Configuration 例項
    下面僅列出得到 SqlSession 的方法

openSessionFromConnection: 透過資料庫連線建立物件的 SqlSession 物件
Connection -> new Transaction -> new Executor -> new DefaultSqlSession,Transaction & Executor & DefaultSqlSession 一一對應

// 從 Connection 獲取 SqlSession 例項
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
  try {
    boolean autoCommit;
    try {
      autoCommit = connection.getAutoCommit();
    } catch (SQLException e) {
      // Failover to true, as most poor drivers
      // or databases won't support transactions
      autoCommit = true;
    }
    // Environment 儲存了對應 XML 標籤的內容,即資料庫連線相關配置
    final Environment environment = configuration.getEnvironment();
    // 主要是做了如果沒有配置就使用 ManagedTransactionFactory, 因為是直接使用資料庫連線獲取的, Managed 本身又是資料庫相關由外部控制的,因此可直接 new 它而不需要其他配置
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    // 事務管理: 封裝 Connection 的提交回滾等操作
    final Transaction tx = transactionFactory.newTransaction(connection);
    // 執行器,根據執行器型別建立不同的執行器例項,執行器例項持有對應的事務物件
    final Executor executor = configuration.newExecutor(tx, execType);
	// 三個引數: 配置物件、執行器物件、是否自動提交
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

openSessionFromDataSource

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

SqlSession

  1. getMapper 獲得 Dao/Mapper 直接操作
  2. 其它的一般都是 MyBatis 自己呼叫的介面,Dao/Mapper 的操作實際底層也是委託到他們去呼叫,說白了 Dao/Mapper 是持有對應 SqlSession 例項的
public interface SqlSession extends Closeable {
  <T> T selectOne(String statement);
  // 其他 selectOne 略
  <E> List<E> selectList(String statement);
  <T> Cursor<T> selectCursor(String statement);
  void select(String statement, Object parameter, ResultHandler handler);
  // 其他略
  // insert、update、delete
  void commit();
  void rollback();
  Configuration getConfiguration();
  // 獲取 Dao/Mapper
  <T> T getMapper(Class<T> type);
  // 獲取資料庫連線物件
  Connection getConnection();
}

建構函式

// 持有配置物件,執行器物件
private final Configuration configuration;
private final Executor executor;

private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  this.configuration = configuration;
  this.executor = executor;
  this.dirty = false;
  this.autoCommit = autoCommit;
}

public DefaultSqlSession(Configuration configuration, Executor executor) {
  this(configuration, executor, false);
}

selectOne/selectList
其他也差不多,基本上都是委託給 executor 執行的

private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  try {
    // statement: 類名 + 方法名, 就是 XML 中一個方法的 id
    // parameter: =MapperMethod.ParamMap,方法引數
    // rowBounds: 分頁引數
    // handler: 返回值處理
    MappedStatement ms = configuration.getMappedStatement(statement);
    dirty |= ms.isDirtySelect();
    return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

getMapper
Configuration 有 mapperRegistry 例項,所有 Mapper 都需要註冊到 mapperRegistry 才能使用
Mapper 繫結 SqlSession,每次 getMapper 都是一個新的 Mapper 例項,不是單例

public <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}

public <T> T Configuration.getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

public <T> T MapperRegistry.getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

Mapper 例項都是代理,持有 SqlSession,於是可以直接透過 SqlSession 執行 SQL

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

相關文章