首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

MyBatis 進階實戰(二):動態 SQL、類型別名、Mapper 掃描與封裝查詢對象

     編輯:Java 綜合教學

MyBatis 進階實戰(二):動態 SQL、類型別名、Mapper 掃描與封裝查詢對象

承接上篇基礎配置,本文深入講解 MyBatis 企業級開發核心特性:外部化資料庫配置、全局設定與類型別名、Mapper 批量掃描、動態 SQL 標籤靈活構建查詢條件、以及封裝複雜查詢參數的最佳實踐。

外部化資料庫配置:db.properties

將資料庫連接參數剝離至獨立屬性文件,便於多環境切換與維護:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

升級版 SqlMapConfig.xml:屬性引用、類型別名、Mapper 批量掃描

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 載入外部屬性文件 -->
<properties resource="db.properties" />

<!-- 全局配置參數:二級緩存、延遲加載等,謹慎調優 -->
<!-- <settings><setting name="" value=""/></settings> -->

<!-- 類型別名:簡寫全限定類名 -->
<typeAliases>
<!-- 單個定義:<typeAlias type="cn.itcast.mybatis.po.User" alias="user"/> -->
<!-- 批量掃描包下所有 POJO,別名為類名(大小寫不敏感) -->
<package name="com.mybatis.bean"/>
</typeAliases>

<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<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>
</environment>
</environments>

<!-- Mapper 映射文件註冊三種方式 -->
<mappers>
<!-- 方式一:resource 引用單個 XML,需在 XML 中指定 namespace 為 Mapper 接口全限定名 -->
<!-- <mapper resource="sqlmap/UserMapper.xml" /> -->

<!-- 方式二:class 直接指定 Mapper 接口,約定:XML 與接口同目錄同名 -->
<!-- <mapper class="cn.itcast.mybatis.mapper.UserMapper"/> -->

<!-- 方式三(推薦):批量掃描包,同樣遵循 XML 與接口同目錄同名約定 -->
<package name="com.mybatis.mapper"/>
</mappers>
</configuration>

Mapper 接口:定義豐富的查詢方法

public interface UserMapper {
    User findUserById(int id) throws Exception;
    List<User> findUserList(String name) throws Exception;
    Integer insertUser(User user) throws Exception;
    void deleteUser(int id) throws Exception;
    void updateUser(User user) throws Exception;
    
    // 封裝查詢條件的多種方式
    List<User> findUserByBean(User user) throws Exception;
    List<User> findUserByMap(Map<String, Object> map) throws Exception;
    List<User> findUserByCustom(QueryVo queryVo) throws Exception;
    
    // 動態更新:僅更新非空字段
    void updateUserSet(User user) throws Exception;
}

動態 SQL 精華:UserMapper.xml 深度解析

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.mapper.UserMapper">

<!-- SQL 片段:抽取可復用的查詢條件 -->
<sql id="query_for_user">
<if test="user != null">
    <if test="user.name != null and user.name != ''">
        and name like '%${user.name}%'
    </if>
    <if test="user.sex != null and user.sex != ''">
        and sex = #{user.sex}
    </if>
</if>

<if test="ids != null">
    <foreach collection="ids" separator="or" item="item" open="and (" close=")">
        id = #{item}
    </foreach>
</if>
</sql>

<select id="findUserById" parameterType="int" resultType="User">
SELECT * FROM USER WHERE id = #{id}
</select>

<select id="findUserList" parameterType="String" resultType="User">
select * from user where name like '%${value}%'
</select>

<insert id="insertUser" parameterType="User">
<selectKey keyProperty="id" order="AFTER" resultType="Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(name,age,sex) values(#{name},#{age},#{sex})
</insert>

<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>

<update id="updateUser" parameterType="User">
update user set name=#{name},age=#{age},sex=#{sex} where id=#{id}
</update>

<!-- POJO 封裝查詢 -->
<select id="findUserByBean" parameterType="User" resultType="User">
select * from user where name like '%${name}%' and sex = #{sex}
</select>

<!-- Map 封裝查詢 -->
<select id="findUserByMap" parameterType="hashmap" resultType="User">
select * from user where name like '%${name}%' and age >= #{age}
</select>

<!-- QueryVo 封裝查詢:引用 SQL 片段 + <where> 標籤智能處理 AND/OR -->
<select id="findUserByCustom" parameterType="QueryVo" resultType="User">
select * from user
<where>
    <include refid="query_for_user"/>
</where>
</select>

<!-- 動態更新:<set> 標籤智能處理逗號 -->
<update id="updateUserSet" parameterType="User">
update user
<set>
    <if test="name != null and name != ''">
        name = #{name},
    </if>
    <if test="age != null and age != '' and age != 0">
        age = #{age},
    </if>
    <if test="sex != null and sex != ''">
        sex = #{sex},
    </if>
</set>
where id = #{id}
</update>

</mapper>

核心動態 SQL 標籤詳解

標籤作用關鍵屬性
<where>智能插入 WHERE 關鍵字,自動去除首個 AND/OR無
<set>智能插入 SET 關鍵字,自動去除尾部逗號無
<if>條件判斷test:OGNL 表達式
<foreach>集合遍歷,構建 IN 條件等collection、item、separator、open、close
<include>引用 <sql> 片段refid
<choose>/<when>/<otherwise>互斥分支選擇(類似 switch)test
<trim>通用字串修飾,可替代 where/setprefix、suffix、prefixOverrides、suffixOverrides

參數封裝對象:QueryVo、User、UserCustom

QueryVo.java - 查詢條件封裝類,支援多條件組合:

public class QueryVo {
    private User user;           // 基本查詢條件
    private UserCustom uc;       // 擴展查詢條件
    private List<Integer> ids;   // ID 列表,用於 IN 查詢
    // Getter/Setter 省略
}

UserCustom.java - 擴展實體類(命名慣例:XxxCustom),繼承 User 新增業務字段:

public class UserCustom extends User {
    private String youngOrOld;  // 年齡段標識:年輕/年長
    // Getter/Setter 省略
}

#{ } vs ${ } 核心差異與適用場景

  • #{ }:預編譯佔位符,JDBC PreparedStatement 參數化,防止 SQL 注入,參數名可任意,首選方案
  • ${ }:字串拼接,原值直接拼入 SQL,**無法防注入**,參數名必須為 value(簡單類型時)
  • 必須用 ${ } 的場景:動態表名 select * from ${tableName}、動態排序欄位 order by ${column}、資料庫函數參數 to_date('${date}', 'yyyy-MM-dd')

避坑指南

  • parameterMap 已廢棄,勿使用
  • resultMap 複雜場景才用,簡單映射 resultType 即可
  • 返回 Map 類型需硬編碼 Key,維護性差,建議返回實體或 DTO
  • 動態 SQL 中 OGNL 表達式區分大小寫,屬性名需與實體完全一致

完整代碼已上傳 GitHub:https://github.com/leechenxiang/mybatis002-dynamic-proxy

掌握動態 SQL 與參數封裝,是 MyBatis 從「能用」到「好用」的關鍵躍遷。下一篇將講解 MyBatis 與 Spring 深度整合、Mapper 動態代理機制原理、緩存架構設計等進階主題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved