首頁 > 軟體

MyBatis連線池的深入和動態SQL詳解

2022-02-09 10:00:49

一,Mybatis 連線池與事務深入

1.1 Mybatis 的連線池技術

Mybatis 中連線池技術,它採用的是自己的連線池技術。在 Mybatis 的 SqlMapConfig.xml 組態檔中,通過來實現 Mybatis 中連線池的設定。

1.1.1 Mybatis 連線池的分類

在 Mybatis 中我們將它的資料來源 dataSource 分為以下幾類:

可以看出 Mybatis 將它自己的資料來源分為三類: 

  • UNPOOLED 不使用連線池的資料來源 
  • POOLED 使用連線池的資料來源 
  • JNDI 使用 JNDI 實現的資料來源

具體結構如下:

相應地,MyBatis 內部分別定義了實現了 java.sql.DataSource 介面的 UnpooledDataSource,PooledDataSource 類來表示 UNPOOLED、POOLED 型別的資料來源。

在這三種資料來源中,我們一般採用的是 POOLED 資料來源(很多時候我們所說的資料來源就是為了更好的管理資料庫連線,也就是我們所說的連線池技術)。

1.1.2 Mybatis 中資料來源的設定

MyBatis 在初始化時,根據的 type 屬性來建立相應型別的的資料來源 DataSource,即:

  • type=”POOLED”:MyBatis 會建立 PooledDataSource 範例
  • type=”UNPOOLED” : MyBatis 會建立 UnpooledDataSource 範例
  • type=”JNDI”:MyBatis 會從 JNDI 服務上查詢 DataSource 範例,然後返回使用

我們的資料來源設定就是在 SqlMapConfig.xml 檔案中,具體設定如下:

<!-- 設定資料來源(連線池)資訊 --> 
<dataSource type="POOLED"> 
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>

1.2 Mybatis 的事務控制

1.2.1 JDBC 中事務的回顧

在 JDBC 中我們可以通過手動方式將事務的提交改為手動方式,通過 setAutoCommit()方法就可以調整。

通過 JDK 檔案,我們找到該方法如下:

那麼我們的 Mybatis 框架因為是對 JDBC 的封裝,所以 Mybatis 框架的事務控制方式,本身也是用 JDBC 的setAutoCommit()方法來設定事務提交方式的。

1.2.2 Mybatis 中事務提交方式

Mybatis 中事務的提交方式,本質上就是呼叫 JDBC 的 setAutoCommit()來實現事務控制。

我們執行之前所寫的程式碼:

@Test
public void testSaveUser() throws Exception {
User user = new User();
user.setUsername("mybatis user09");
//6.執行操作
int res = userDao.saveUser(user);
System.out.println(res);
System.out.println(user.getId());
}
@Before//在測試方法執行之前執行
public void init()throws Exception {
//1.讀取組態檔
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.建立構建者物件
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.建立 SqlSession 工廠物件
factory = builder.build(in);
//4.建立 SqlSession 物件
session = factory.openSession();
//5.建立 Dao 的代理物件
userDao = session.getMapper(IUserDao.class);
}
@After//在測試方法執行完成之後執行
public void destroy() throws Exception{
//7.提交事務
session.commit();
//8.釋放資源
session.close();
in.close();
}

預設 setAutoCommit()方法,在執行時它的值被設定為 false 了,所以我們在 CUD 操作中,必須通過 sqlSession.commit()方法來執行提交操作。

1.2.3 Mybatis 自動提交事務的設定

通過上面的研究和分析,現在我們一起思考,為什麼 CUD 過程中必須使用 sqlSession.commit()提交事務?主要原因就是在連線池中取出的連線,都會將呼叫 connection.setAutoCommit(false)方法,這樣我們就必須使用 sqlSession.commit()方法,相當於使用了 JDBC 中的 connection.commit()方法實現事務提交。

明白這一點後,我們現在一起嘗試不進行手動提交,一樣實現 CUD 操作。

@Before//在測試方法執行之前執行
public void init()throws Exception {
//1.讀取組態檔
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.建立構建者物件
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.建立 SqlSession 工廠物件
factory = builder.build(in);
//4.建立 SqlSession 物件
**session = factory.openSession(true);**
//5.建立 Dao 的代理物件
userDao = session.getMapper(IUserDao.class);
}
@After//在測試方法執行完成之後執行
public void destroy() throws Exception{
//7.釋放資源
session.close();
in.close();
}

事務就設定為自動提交了,同樣可以實現CUD操作時記錄的儲存。雖然這也是一種方式,但就程式設計而言,設定為自動提交方式為 false 再根據情況決定是否進行提交,這種方式更常用。因為我們可以根據業務情況來決定提交是否進行提交。

二.Mybatis 的動態 SQL 語句

Mybatis 的對映檔案中,前面我們的 SQL 都是比較簡單的,有些時候業務邏輯複雜時,我們的 SQL 是動態變化的,此時在前面的學習中我們的 SQL 就不能滿足要求了。

參考的官方檔案,描述如下:

2.1 動態 SQL 之標籤

我們根據實體類的不同取值,使用不同的 SQL 語句來進行查詢。比如在 id 如果不為空時可以根據 id 查詢,如果 username 不同空時還要加入使用者名稱作為條件。這種情況在我們的多條件組合查詢中經常會碰到。

2.1.1 持久層 Dao 介面

/**
* 根據使用者資訊,查詢使用者列表
* @param user
* @return
*/
List<User> findByUser(User user);

2.1.2 持久層 Dao 對映設定

<select id="findByUser" resultType="user" parameterType="user">
select * from user where 1=1
<if test="username!=null and username != '' ">
and username like #{username}
</if> <if test="address != null">
and address like #{address}
</if>
</select>

注意:<if>標籤的 test 屬性中寫的是物件的屬性名,如果是包裝類的物件要使用 OGNL 表示式的寫法。

另外要注意 where 1=1 的作用~! 

2.1.3 測試

@Test
public void testFindByUser() {
User u = new User();
u.setUsername("%王%");
u.setAddress("%順義%");
//6.執行操作
List<User> users = userDao.findByUser(u);
for(User user : users) {
System.out.println(user);
} }

2.2 動態 SQL 之標籤

為了簡化上面 where 1=1 的條件拼裝,我們可以採用標籤來簡化開發。

2.2.1 持久層 Dao 對映設定

<!-- 根據使用者資訊查詢 --> 
<select id="findByUser" resultType="user" parameterType="user"> <include refid="defaultSql">
</include> <where> <if test="username!=null and username != '' ">
and username like #{username}
</if> <if test="address != null">
and address like #{address}
</if>
</where>
</select>

2.3 動態標籤之標籤

需求

傳入多個 id 查詢使用者資訊,用下邊兩個 sql 實現:

SELECT * FROM USERS WHERE username LIKE ‘%張%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE ‘%張%' AND id IN (10,89,16)

這樣我們在進行範圍查詢時,就要將一個集合中的值,作為引數動態新增進來。

這樣我們將如何進行引數的傳遞?

2.3.1 在 QueryVo 中加入一個 List 集合用於封裝引數

/**
* 
* <p>Title: QueryVo</p>
* <p>Description: 查詢的條件</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class QueryVo implements Serializable {
private List<Integer> ids;
public List<Integer> getIds() {
return ids; }
public void setIds(List<Integer> ids) {
this.ids = ids; } }

2.3.2 持久層 Dao 介面

/**
* 根據 id 集合查詢使用者
* @param vo
* @return
*/
List<User> findInIds(QueryVo vo);

2.3.3 持久層 Dao 對映設定

<!-- 查詢所有使用者在 id 的集合之中 -->
 <select id="findInIds" resultType="user" parameterType="queryvo">
<!-- select * from user where id in (1,2,3,4,5); -->
 <include refid="defaultSql"></include> 
 <where>
            <if test="list != null and list.size() > 0">
                <foreach collection="list" open="id in ( " close=")" item="uid"
                         separator=",">
                    #{uid}
                </foreach>
            </if>
        </where>
</select>

SQL 語句:

select 欄位 from user where id in (?)

<foreach>標籤用於遍歷集合,它的屬性:

  • collection:代表要遍歷的集合元素,注意編寫時不要寫#{}
  • open:代表語句的開始部分
  • close:代表結束部分
  • item:代表遍歷集合的每個元素,生成的變數名
  • sperator:代表分隔符

2.3.4 編寫測試方法

@Test
public void testFindInIds() {
QueryVo vo = new QueryVo();
List<Integer> ids = new ArrayList<Integer>();
ids.add(41);
ids.add(42);
ids.add(43);
ids.add(46);
ids.add(57);
vo.setIds(ids);
//6.執行操作
List<User> users = userDao.findInIds(vo);
for(User user : users) {
System.out.println(user);
} }

2.4 Mybatis 中簡化編寫的 SQL 片段

Sql 中可將重複的 sql 提取出來,使用時用 include 參照即可,最終達到 sql 重用的目的。

2.4.1 定義程式碼片段

<!-- 抽取重複的語句程式碼片段 -->
 <sql id="defaultSql">
	select * from user
</sql>

2.4.2 參照程式碼片段

<!-- 設定查詢所有操作 --> 
<select id="findAll" resultType="user">
<include refid="defaultSql"></include>
</select>
<!-- 根據 id 查詢 --> <select id="findById" resultType="UsEr" parameterType="int">
<include refid="defaultSql"></include>
where id = #{uid}
</select>

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com