程式碼
// 使用流程
// 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 的例項工廠
- 獲取 MyBatis 配置物件 Configuration
- 可配置
- 自動提交
- 連線物件
- 事務隔離級別
- 執行器型別
兩個實現類: 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
- 不傳是否自動提交,預設不自動提交
- 不傳事務隔離級別,預設使用資料庫的事務隔離級別
- 不傳執行器型別,預設使用 Configuration 的配置(預設是 SIMPLE)
- 儲存了 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
- getMapper 獲得 Dao/Mapper 直接操作
- 其它的一般都是 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);
}