在某些小項目中,需要單獨使用到 mybatis,但是網上常見工具類方法獲取的 session 通常要么需要手動關閉,這樣即麻煩,而且有時又容易出錯,要么要需要結合使用spring,但是我們只想寫個簡單的增刪改,不想引入太多框架。
而下面將要介紹的SqlSessionManager
所獲取的session以及mapper就無需關心連接關閉的事情了。
環境搭建
數據庫與實體類:
![]() |
![]() |
工具類:
public class MybatisUtils {
private static SqlSessionManager sessionManager = null;
static {
try {
InputStream inputStream = MybatisUtils.class.getClassLoader()
.getResourceAsStream("mybatis-config1.xml");
sessionManager = SqlSessionManager.newInstance(inputStream);
} catch (Exception e) {
throw new RuntimeException("the Configuration of Mybatis is not exist!");
}
}
/**
* 獲取 Mapper
* @param <T>
* @param clazz
* @return
*/
public static <T> T getMapper(Class<T> clazz) {
return sessionManager.getMapper(clazz);
}
/**
* 獲取 SqlSessionManager
* @return
*/
public static SqlSessionManager getSessionManager() {
return sessionManager;
}
}
mapper:
public interface AccountMapper {
@Select("select * from account")
public List<Account> selectAccountList();
@Select("select * from account where id = #{id}")
public Account selectAccountById(Long id);
@Update("update account set money = #{money} where id = #{userId}")
public void updateAccount(Account account);
}
原理
它的原理就是使用了JDK的動態代理,先看看SqlSessionManager
的構造方法
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
從上面可以看出SqlSessionManager
使用了Proxy.newProxyInstance()
即JDK動態代理對默認的session進行了增強,增強類為SqlSessionInterceptor
。
下面來結合具體的例子來看一下增強方法
@Test
public void test1() {
AccountMapper mapper = MybatisUtils.getMapper(AccountMapper.class);
List<Account> list = mapper.selectAccountList();
System.out.println(list);
}
上面是一個很簡單的查詢方法,
然后我們在源碼畫線處打一個斷點
然后執行
因為我們並沒有設置localSqlSession
,所以很顯然跳轉到了else塊中,當然localSqlSession
的設置會在后面事務管理中說到。
然后我們看②處代碼,他使用了增強的try語句,可以自動幫我們釋放 session。還有③④處代碼,會發現SqlSessionManager
自動幫我們進行事務的提交與回滾,所以之后我們使用mapper的方法時,可以隨意使用,不再關心連接釋放的問題了。
事務管理
前面說到通過SqlSessionManager
獲取的mapper進行了增強,在我們使用單一的增刪改時就不用再考慮連接釋放的問題了,但是有些問題需要執行多個mapper方法,這時就要通過事務來完成了,並且SqlSessionManager
也為我們提供了一些事務提交回滾的方法,下面就來看看。
假設有個簡單的業務轉賬,用戶1向用戶2轉賬指定金額,當程序出錯時要能回滾,不能一方扣了錢,另一方錢卻沒有增加。
這里最關鍵的就是①處的startManagedSession()
方法,我們能發現這里給localSqlSession
設置了一個值
再回到前面的SqlSessionInterceptor
類中
就能發現,當我們調用了startManagedSession()
方法后,SqlSessionManager
就不會幫助我們進行session的管理,一切連接的關閉,事務的提交與回滾就需要我們自己手動完成了
小結
通過SqlSessionManager
獲取的mapper,使用單一的接口方法操作時,我們可以不用關心連接的釋放。
只有需要多個接口方法組合操作時我們才需要開啟session管理,手動來進行連接的釋放,與事務提交回滾。