MyBatis 自動關閉 session


在某些小項目中,需要單獨使用到 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管理,手動來進行連接的釋放,與事務提交回滾。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM