MyBatis常用物件SqlSessionFactory和SqlSession介紹和運用
前言:
學習框架一個比較好的路徑閱讀原始碼.本文介紹的SqlSessionFactory和SqlSession.可以通過了解SqlSessionFactory介面和SqlSession介面以及兩個的實現類入手,去看原始碼瞭解實現過程.最好能把專案下載到本地,慢慢分析實現過程.
MyBatis的持久化解決方案是將使用者從原始的JDBC訪問中解放出來,使用者只需要定義需要操作的SQL語句,無須關注底層的JDBC操作,就可以以面向物件的方式來進行持久化層操作.底層資料庫連線的獲取,資料訪問的實現,事務控制等都無須使用者關心,從而將應用層從底層的JDBC/JTA API抽取出來.通過配置檔案管理JDBC連線,讓MyBatis解決持久化的實現.在MyBatis中的常見物件有SqlSessionFactory和SqlSession.本文這種介紹一下兩者的概念和使用.
一、 SqlSessionFactory
SqlSessionFactory是MyBatis的關鍵物件,它是個單個數據庫對映關係經過編譯後的記憶體映象.SqlSessionFactory物件的例項可以通過SqlSessionFactoryBuilder物件類獲得,而SqlSessionFactoryBuilder則可以從XML配置檔案或一個預先定製的Configuration的例項構建出SqlSessionFactory的例項.每一個MyBatis的應用程式都以一個SqlSessionFactory物件的例項為核心.同時SqlSessionFactory也是執行緒安全的,SqlSessionFactory一旦被建立,應該在應用執行期間都存在.在應用執行期間不要重複建立多次,建議使用單例模式.SqlSessionFactory是建立SqlSession的工廠.
//SqlSessionFactory介面原始碼如下所示:
package org.apache.ibatis.session;
import java.sql.Connection;
public interface SqlSessionFactory {
SqlSession openSession();//這個方法最經常用,用來建立SqlSession物件.
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
SqlSession是MyBatis的關鍵物件,是執行持久化操作的獨享,類似於JDBC中的Connection.它是應用程式與持久層之間執行互動操作的一個單執行緒物件,也是MyBatis執行持久化操作的關鍵物件.SqlSession物件完全包含以資料庫為背景的所有執行SQL操作的方法,它的底層封裝了JDBC連線,可以用SqlSession例項來直接執行被對映的SQL語句.每個執行緒都應該有它自己的SqlSession例項.SqlSession的例項不能被共享,同時SqlSession也是執行緒不安全的,絕對不能講SqlSeesion例項的引用放在一個類的靜態欄位甚至是例項欄位中.也絕不能將SqlSession例項的引用放在任何型別的管理範圍中,比如Servlet當中的HttpSession物件中.使用完SqlSeesion之後關閉Session很重要,應該確保使用finally塊來關閉它.
//SqlSession介面原始碼如下所示:
package org.apache.ibatis.session;
import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.executor.BatchResult;
public interface SqlSession extends Closeable {
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
void select(String statement, Object parameter, ResultHandler handler);
void select(String statement, ResultHandler handler);
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
int insert(String statement);
int insert(String statement, Object parameter);
int update(String statement);
int update(String statement, Object parameter);
int delete(String statement);
int delete(String statement, Object parameter);
void commit();
void commit(boolean force);
void rollback();
void rollback(boolean force);
List<BatchResult> flushStatements();
void close();
void clearCache();
Configuration getConfiguration();
<T> T getMapper(Class<T> type);
Connection getConnection();
}
三、SqlSessionFactory和SqlSession實現過程
mybatis框架主要是圍繞著SqlSessionFactory進行的,建立過程大概如下:
(1)、定義一個Configuration物件,其中包含資料來源、事務、mapper檔案資源以及影響資料庫行為屬性設定settings
(2)、通過配置物件,則可以建立一個SqlSessionFactoryBuilder物件
(3)、通過 SqlSessionFactoryBuilder 獲得SqlSessionFactory 的例項。
(4)、SqlSessionFactory 的例項可以獲得操作資料的SqlSession例項,通過這個例項對資料庫進行操作
並且如果想按照上述方式得到SqlSessionFactory,最好使用下面的mybatis-config.xml類似的配置.在這裡mybatis-config.xml配置檔案是沒有和Spring配置檔案整合過得,如果專案中mybaits的配置檔案和Spring配置檔案整合過了,則下面的程式碼執行估計會出錯,因為一般spring和mybatis整合過之後,mybatis的配置檔案基本沒有存在的必要了,之前在mybatis中配置的資料來源和事務這兩個方面,一般的做法都會spring的配置檔案,則下面的程式碼載入mybatis-config.xml的時候,得不到必要的資訊,建立的過程中會有問題.所以在這裡先給一份mybatis-config.xml單獨的配置檔案.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 載入類路徑下的屬性檔案 -->
<properties resource="db.properties"/>
<!-- 設定類型別名 -->
<typeAliases>
<typeAlias type="cn.itcast.javaee.mybatis.app04.Student" alias="student"/>
</typeAliases>
<!-- 設定一個預設的連線環境資訊 -->
<environments default="mysql_developer">
<!-- 連線環境資訊,取一個任意唯一的名字 -->
<environment id="mysql_developer">
<!-- mybatis使用jdbc事務管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用連線池方式來獲取連線 -->
<dataSource type="pooled">
<!-- 配置與資料庫互動的4個必要屬性 -->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
<!-- 連線環境資訊,取一個任意唯一的名字 -->
<environment id="oracle_developer">
<!-- mybatis使用jdbc事務管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用連線池方式來獲取連線 -->
<dataSource type="pooled">
<!-- 配置與資料庫互動的4個必要屬性 -->
<property name="driver" value="${oracle.driver}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.username}"/>
<property name="password" value="${oracle.password}"/>
</dataSource>
</environment>
</environments>
<!-- 載入對映檔案-->
<mappers>
<mapper resource="cn/itcast/javaee/mybatis/app14/StudentMapper.xml"/>
</mappers>
</configuration>
下面的這行程式碼功能是通過配置檔案mybatis-config.xml,建立SqlSessionFactory物件,然後產生SqlSession,執行SQL語句.而mybatis的初始化發生在:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
如果是spring和mybaits整合之後的配置檔案,一般以這種方式實現,SqlSessionFactory的建立:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- 自動掃描mapping.xml檔案 -->
<property name="mapperLocations" value="classpath:com/cn/mapper/*.xml"></property>
</bean>
關於SqlSessionFactory和SqlSession兩個物件給一個具體的使用過程:
package com.cn.testIUserService;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.cn.entity.User;
public class MyBatisTest {
public static void main(String[] args) {
try {
//讀取mybatis-config.xml檔案
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//初始化mybatis,建立SqlSessionFactory類的例項
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//建立session例項
SqlSession session = sqlSessionFactory.openSession();
/*
* 接下來在這裡做很多事情,到目前為止,目的已經達到得到了SqlSession物件.通過呼叫SqlSession裡面的方法,
* 可以測試MyBatis和Dao層介面方法之間的正確性,當然也可以做別的很多事情,在這裡就不列舉了
*/
//插入資料
User user = new User();
user.setC_password("123");
user.setC_username("123");
user.setC_salt("123");
//第一個引數為方法的完全限定名:位置資訊+對映檔案當中的id
session.insert("com.cn.dao.UserMapping.insertUserInformation", user);
//提交事務
session.commit();
//關閉session
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
針對上面的程式碼給出詳細的說明關於SqlSessionFactory和SqlSession建立過程涉及的內容.
結合上述SqlSessionFactory和SqlSession使用過程和結構圖,涉及到的方法為下面步驟,結合原始碼中的方法為下面的步驟:
第一步首先SqlSessionFactoryBuilder去讀取mybatis的配置檔案,然後build一個DefaultSqlSessionFactory,即得到SqlSessionFactory
//原始碼中涉及的包和具體方法為:
//涉及的包為:package org.apache.ibatis.session;
//第一個類為:SqlSessionFactoryBuilder,設計到此類的方法為下面部分:
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//通過XMLConfigBuilder解析配置檔案,解析的配置相關資訊都會封裝為一個Configuration物件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//然後返回一個DefaultSqlSessionFactory
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
//得到DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
//第二個類為:DefaultSqlSessionFactory,涉及的方法為:
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
第二步,獲取到SqlSessionFactory之後,就可以利用SqlSessionFactory方法的openSession來獲取SqlSession物件了。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//通過Confuguration物件去獲取Mybatis相關配置資訊, Environment物件包含了資料來源和事務的配置
// execType為執行器型別,配置檔案中定義
// SimpleExecutor -- SIMPLE 就是普通的執行器。
//ReuseExecutor -執行器會重用預處理語句(prepared statements)
//BatchExecutor --它是批量執行器
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//定義執行器,是對statement的封裝
final Executor executor = configuration.newExecutor(tx, execType);
//最後返回一個SqlSession
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物件之後就可以利用SqlSession內部的方法進行CRUD操作了。
注意一點,Connection物件是在SqlSession物件建立之後進行CURD操作中建立的。深入查詢之後找到在ManagedTransaction類中找到獲取Connection物件的關鍵程式碼如下:
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
//dataSource 來源有三種,JndiDatasource,PooledDataSource,UnpooledDataSource,配置檔案中定義
this.connection = this.dataSource.getConnection();
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
}
}
PooledDataSource和UnPooledDataSource的區別是PooledDataSource使用了連線池。為什麼使用連線池呢?因為建立一個Connection物件的過程,在底層就相當於和資料庫建立的通訊連線,在建立通訊連線的過程,消耗了非常多的時間,而往往我們建立連線後(即建立Connection物件後),就執行一個簡單的SQL語句,然後就要拋棄掉,這是一個非常大的資源浪費!mybatis針對這一個問題提出的PooledDataSource使用了連線池。關於資料庫連線池的知識點,可以自行百度,在這裡就不擴充套件介紹了.