Java 持久層框架之 MyBatis

暮夏有五發表於2020-12-22

MyBatis 簡介

MyBatis 是一個基於 Java 的持久層框架,它內部封裝了 JDBC,使開發者只需關注 SQL 語句本身,而不用再花費精力去處理諸如註冊驅動、建立 Connection、配置 Statement 等繁雜過程。

Mybatis 通過 xml 或註解的方式將要執行的各種 StatementPreparedStatement 等配置起來,並通過 Java 物件和 StatementSQL 的動態引數進行對映生成最終執行的 SQL 語句,最後由 MyBatis 框架執行 SQL 並將結果對映成 Java 物件並返回。

MyBatis 與 Hibernate

Hibernate 框架是提供了全面的資料庫封裝機制的 全自動 ORM,即實現了 POJO 和資料庫表之間的對映,以及 SQL 的自動生成和執行。

相對於此,MyBatis 只能算作是 半自動 ORM。其著力點,是在 POJO 類與 SQL 語句之間的對映關係。也就是說,MyBatis 並不會為程式設計師自動生成 SQL 語句。具體的 SQL 需要程式設計師自己編寫,然後通過 SQL 語句對映檔案,將 SQL 所需的引數,以及返回的結果欄位對映到指定 POJO。

MyBatis 特點

  • XML 檔案中配置 SQL 語句,實現了 SQL 語句與程式碼的分離,給程式的維護帶來了很大便利。
  • 可以結合資料庫自身的特點靈活控制 SQL 語句,因此能夠實現比 Hibernate 等全自動 ORM 框架更高的查詢效率,能夠完成複雜查詢。

file

Spring 整合 Mybatis

引入依賴

pom.xml 引入 Mybatis 相關依賴。

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.8</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.3.17.RELEASE</version>
</dependency>

建立 Mybatis 配置檔案

resource 目錄下建立 mybatis-config 配置檔案。

<?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>
    <!-- 全域性引數 -->
    <settings>
        <!-- 列印 SQL 語句 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    
        <!-- 使全域性的對映器啟用或禁用快取。 -->
        <setting name="cacheEnabled" value="false"/>

        <!-- 全域性啟用或禁用延遲載入。當禁用時,所有關聯物件都會即時載入。 -->
        <setting name="lazyLoadingEnabled" value="true"/>

        <!-- 當啟用時,有延遲載入屬性的物件在被呼叫時將會完全載入任意屬性。否則,每種屬性將會按需要載入。 -->
        <setting name="aggressiveLazyLoading" value="true"/>

        <!-- 是否允許單條 SQL 返回多個資料集 (取決於驅動的相容性) default:true -->
        <setting name="multipleResultSetsEnabled" value="true"/>

        <!-- 是否可以使用列的別名 (取決於驅動的相容性) default:true -->
        <setting name="useColumnLabel" value="true"/>

        <!-- 允許 JDBC 生成主鍵。需要驅動器支援。如果設為了 true,這個設定將強制使用被生成的主鍵,有一些驅動器不相容不過仍然可以執行。 default:false  -->
        <setting name="useGeneratedKeys" value="false"/>

        <!-- 指定 MyBatis 如何自動對映 資料基表的列 NONE:不對映 PARTIAL:部分 FULL:全部  -->
        <setting name="autoMappingBehavior" value="PARTIAL"/>

        <!-- 這是預設的執行型別 (SIMPLE: 簡單; REUSE: 執行器可能重複使用prepared statements語句;BATCH: 執行器可以重複執行語句和批量更新) -->
        <setting name="defaultExecutorType" value="SIMPLE"/>

        <!-- 使用駝峰命名法轉換欄位。 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>

        <!-- 設定本地快取範圍 session:就會有資料的共享 statement:語句範圍 (這樣就不會有資料的共享 ) defalut:session -->
        <setting name="localCacheScope" value="SESSION"/>

        <!-- 設定 JDBC 型別為空時,某些驅動程式 要指定值, default:OTHER,插入空值時不需要指定型別 -->
        <setting name="jdbcTypeForNull" value="NULL"/>
    </settings>
</configuration>

建立整合配置檔案

resource 目錄下建立一個 spring-context-mybatis.xml 的整合配置檔案。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 配置 SqlSession -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 用於配置對應實體類所在的包,多個 package 之間可以用 ',' 號分割 -->
        <property name="typeAliasesPackage" value="com.antoniopeng.ssm.domain"/>
        <!-- 用於配置物件關係對映-->
        <property name="mapperLocations" value="classpath:/mapper/**/*.xml"/>
				<!-- -用於配置檔案所在目錄->
        <property name="configLocation" value="classpath:/mybatis-config.xml"></property>
    </bean>

    <!-- 掃描 Mapper -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.antoniopeng.ssm.dao" />
    </bean>
</beans>

CRUD 案例

新增

<insert id="insert">
    INSERT INTO tb_user (
      id,
      username,
      password,
      phone,
      email,
      created,
      updated
    )
    VALUES
      (
        #{id},
        #{username},
        #{password},
        #{phone},
        #{email},
        #{created},
        #{update}
      )
</insert>

刪除

<delete id="delete">
    DELETE FROM tb_user WHERE id = #{id}
</delete>

查詢

<select id="getById" resultType="TbUser">
    SELECT
      a.id,
      a.username,
      a.password,
      a.phone,
      a.email,
      a.created,
      a.updated AS "update"
    FROM
      tb_user AS a
    WHERE
      a.id = #{id}
</select>

更新

<update id="update">
    UPDATE
      tb_user
    SET
      username = #{username},
      password = #{password},
      phone = #{phone},
      email = #{email},
      created = #{created},
      updated = #{update}
    WHERE id = #{id}
</update>

MyBatis 動態 SQL

動態 SQL 主要用於解決查詢條件不確定的情況:在程式執行期間,根據使用者提交的查詢條件進行查詢。提交的查詢條件不同,執行的 SQL 語句不同。若將每種可能的情況均逐一列出,對所有條件進行排列組合,將會出現大量的 SQL 語句。此時,可使用動態 SQL 來解決這樣的問題。這裡的條件判斷使用的表示式為 OGNL 表示式。常用的動態 SQL 標籤有 <if><where><choose><foreach> 等。

注意:在 mapper 的動態 SQL 中若出現 ><>=<= 等符號,最好將其轉換為實體符號。否則,XML 可能會出現解析出錯問題,特別是 < 在 XML 中是絕對不能出現的。

if 標籤

對於該標籤的執行,當 test 的值為 true 時,會將其包含的 SQL 片斷拼接到其所在的 SQL 語句中。
案例
為了解決兩個條件均未做設定的情況,在 where 後新增了一個“1=1”的條件。這樣就不至於兩個條件均未設定而出現只剩下一個 where,而沒有任何可拼接的條件的不完整 SQL 語句。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.antoniopeng.ssm.dao.StudentDao">
    <!-- if -->
    <select id="selectByIf" resultType="com.antoniopeng.ssm.entity.Student">
        SELECT 
				    *
        FROM
            student
        WHERE 1 = 1
        <if test="name != null and name != ''">
            AND name LIKE concat('%', #{name}, '%')
        </if>
        <if test="age != null and age > 0">
            AND age > #{age}
        </if>
    </select>
</mapper>

where 標籤

標籤的中存在一個比較麻煩的地方:需要在 where 後手工新增 1=1 的子句。因為,若 where 後的所有 條件均為 false,而 where 後若又沒有 1=1 子句,則 SQL 中就會只剩下一個空的 where,SQL 出錯。所以,在 where 後,需要新增永為真子句 1=1,以防止這種情況的發生。但當資料量很大時,會嚴重影響查詢效率。
案例

<select id="selectByWhere" resultType="com.antoniopeng.ssm.entity.Student">
    SELECT
		    *
    FROM
      student
    <where>
        <if test="name != null and name != ''">
            AND name LIKE concat('%', #{name}, '%')
        </if>
        <if test="age != null and age > 0">
            AND age > #{age}
        </if>
    </where>
</select>

choose 標籤

該標籤中只可以包含 <when/><otherwise/>,可以包含多個 <when/> 與一個 <otherwise/>。它們聯合使用,完成 Java 中的開關語句 switch case 功能。
案例
本例要完成的需求是,若姓名不空,則按照姓名查詢;若姓名為空,則按照年齡查詢;若沒有查詢條件,則沒有查詢結果。

<!-- choose -->
<select id="selectByChoose" resultType="com.antoniopeng.ssm.entity.Student">
    SELECT
        *
    FROM
      student
    <where>
        <choose>
            <when test="name != null and name != ''">
                AND name LIKE concat('%', #{name}, '%')
            </when>
            <when test="age != null and age > 0">
                AND age > #{age}
            </when>
            <otherwise>
                AND 1 != 1
            </otherwise>
        </choose>
    </where>
</select>

foreach 標籤

該標籤用於實現對於陣列與集合的遍歷。對其使用,需要注意:collection 表示要遍歷的集合型別,這裡是陣列,即 array。opencloseseparator 為對遍歷內容的 SQL 拼接。
遍歷陣列案例

<!-- foreach -->
<select id="selectByForeach" resultType="com.antoniopeng.ssm.entity.Student">
    SELECT
        *
    FROM
      student
    <if test="array != null and array.length > 0">
        WHERE id IN
        <foreach collection="array" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </if>
</select>

遍歷泛型為基本型別的集合案例

/**
 * 使用 foreach 標籤以 list 基本型別的形式查詢
 * @param ids
 * @return
 */
public List<Student> selectByForeachWithListBase(List<Long> ids);
<!-- foreach -->
<select id="selectByForeachWithListBase" resultType="com.antoniopeng.ssm.entity.Student">
    SELECT
        *
    FROM
      student
    <if test="list != null and list.size > 0">
        WHERE id IN
        <foreach collection="list" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </if>
</select>

遍歷泛型為自定義型別的集合案例

/**
 * 使用 foreach 標籤以 list 自定義型別的形式查詢
 * @param students
 * @return
 */
public List<Student> selectByForeachWithListCustom(List<Student> students);
<!-- foreach -->
<select id="selectByForeachWithListCustom" resultType="com.antoniopeng.ssm.entity.Student">
    SELECT
		    *
    FROM
      student
    <if test="list != null and list.size > 0">
        WHERE id IN
        <foreach collection="list" open="(" close=")" item="student" separator=",">
            #{student.id}
        </foreach>
    </if>
</select>

sql 標籤

該標籤用於定義 SQL 片斷,以便其它 SQL 標籤複用。而其它標籤使用該 SQL 片斷, 需要使用 <include/> 子標籤。該標籤可以定義 SQL 語句中的任何部分,所以 <include/> 子標籤可以放在動態 SQL 的任何位置。
案例

<sql id="select">
    SELECT
        id,
        name,
        age,
        score
    FROM
      student
</sql>

使用 sql 標籤

<!-- foreach -->
<select id="selectByForeachWithListCustom" resultType="com.antoniopeng.ssm.entity.Student">
    <include refid="select" />
</select>
  • 文章作者:彭超
  • 本文首發於個人部落格:https://antoniopeng.com
  • 版權宣告:本部落格所有文章除特別宣告外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 彭超的部落格

相關文章