虛擬化KVM常用命令匯總
還在為 MyBatis 的復(fù)雜配置頭疼?SQL 映射總是寫得亂七八糟?分頁查詢、動態(tài) SQL、關(guān)聯(lián)查詢這些場景總是搞不定?別慌!今天這篇文章,我把 MyBatis 從基礎(chǔ)到高級的 25 個實用配置和 SQL 映射案例一次性講透,每個案例都配上真實業(yè)務(wù)場景的代碼示例,看完讓你對 MyBatis 的使用豁然開朗,開發(fā)效率直接翻倍!

一、基礎(chǔ)配置:搭建 MyBatis 核心環(huán)境
1. 核心配置文件 mybatis-config.xml 基礎(chǔ)結(jié)構(gòu)
MyBatis 的全局配置文件,包含數(shù)據(jù)源、別名、Mapper 注冊等核心配置:
<?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>
<!-- 環(huán)境配置:可配置多個環(huán)境,默認使用development -->
<environments default="development">
<environment id="development">
<!-- 事務(wù)管理器:JDBC 事務(wù)管理 -->
<transactionManager type="JDBC"/>
<!-- 數(shù)據(jù)源:POOLED 表示使用連接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 注冊 Mapper 文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<mapper class="com.example.mapper.OrderMapper"/> <!-- 接口映射 -->
</mappers>
</configuration>關(guān)鍵說明:POOLED 數(shù)據(jù)源會緩存數(shù)據(jù)庫連接,避免頻繁創(chuàng)建連接的性能損耗,生產(chǎn)環(huán)境必用。
2. 映射文件 Mapper.xml 基礎(chǔ)結(jié)構(gòu)
定義 SQL 語句和結(jié)果映射的核心文件,與 Mapper 接口對應(yīng):
<?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">
<!-- namespace 必須與 Mapper 接口全類名一致 -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- 根據(jù) ID 查詢用戶 -->
<select id="getUserById" parameterType="Long" resultType="com.example.pojo.User">
SELECT id, username, age FROM user WHERE id = #{id}
</select>
</mapper>核心規(guī)則:namespace 必須與 Mapper 接口的全類名一致,id 必須與接口方法名一致,參數(shù)和返回值類型要匹配。
3. 接口綁定:無需實現(xiàn)類直接調(diào)用
MyBatis 最核心的特性之一,通過接口與 XML 映射文件綁定,直接調(diào)用接口方法執(zhí)行 SQL:
// Mapper 接口
public interface UserMapper {
User getUserById(Long id);
}
// 調(diào)用方式
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1L); // 直接調(diào)用接口方法優(yōu)勢:告別傳統(tǒng)的 DAO 實現(xiàn)類,代碼量減少 50%,專注于 SQL 編寫。
二、SQL 映射基礎(chǔ):CRUD 操作實戰(zhàn)
4. 新增用戶并返回自增主鍵
插入數(shù)據(jù)后需要獲取自動生成的主鍵(如 ID),用 useGeneratedKeys 和 keyProperty 配置:
<!-- 新增用戶并返回自增 ID -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, age) VALUES (#{username}, #{age})
</insert>// 調(diào)用后 user.getId() 即可獲取自增主鍵
User user = new User();
user.setUsername("張三");
user.setAge(25);
userMapper.insertUser(user);
System.out.println("新增用戶 ID:" + user.getId()); // 輸出插入后的 ID5. 根據(jù) ID 更新用戶信息
使用 set 標簽處理動態(tài)更新,自動忽略空值字段:
<!-- 更新用戶信息(只更新非空字段) -->
<update id="updateUser">
UPDATE user
<set>
<if test="username != null">username = #{username},</if>
<if test="age != null">age = #{age}</if>
</set>
WHERE id = #{id}
</update>優(yōu)勢:如果 username 為 null,會自動忽略該字段,避免將字段更新為 null。
6. 根據(jù) ID 刪除用戶
簡單的刪除操作,注意參數(shù)傳遞方式:
<!-- 根據(jù) ID 刪除用戶 -->
<delete id="deleteUserById" parameterType="Long">
DELETE FROM user WHERE id = #{id}
</delete>userMapper.deleteUserById(1L);
sqlSession.commit(); // 增刪改必須提交事務(wù)7. 批量刪除用戶(傳入 ID 列表)
用 foreach 標簽處理批量刪除,遍歷集合生成 SQL:
<!-- 批量刪除用戶 -->
<delete id="batchDelete">
DELETE FROM user WHERE id IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>List<Long> ids = Arrays.asList(1L, 2L, 3L);
userMapper.batchDelete(ids); // 一次刪除多個用戶foreach 參數(shù)說明:
- collection:集合參數(shù)名稱(list、array 或自定義名稱)
- item:遍歷的元素變量名
- open/close:包裹整個集合的符號(如 ( 和 ))
- separator:元素之間的分隔符(如 ,)
三、參數(shù)處理:多種參數(shù)傳遞方式
8. 單個參數(shù)傳遞
直接使用 #{參數(shù)名} 接收,參數(shù)名可任意(建議與方法參數(shù)名一致):
<!-- 單個參數(shù):根據(jù)用戶名查詢 -->
<select id="getUserByUsername" resultType="User">
SELECT id, username, age FROM user WHERE username = #{username}
</select>User user = userMapper.getUserByUsername("張三");9. 多個參數(shù)傳遞(@Param 注解)
方法有多個參數(shù)時,用 @Param 注解指定參數(shù)名稱,避免參數(shù)混淆:
// 接口方法(多個參數(shù)用 @Param 注解)
User getUserByUsernameAndAge(@Param("username") String username, @Param("age") Integer age);<!-- 接收多個參數(shù) -->
<select id="getUserByUsernameAndAge" resultType="User">
SELECT id, username, age FROM user
WHERE username = #{username} AND age = #{age}
</select>10. 傳遞 JavaBean 參數(shù)
直接傳遞實體類對象,用 #{屬性名} 接收參數(shù):
// 傳遞 User 對象作為參數(shù)
List<User> getUsersByCondition(User user);<!-- 使用 JavaBean 屬性作為參數(shù) -->
<select id="getUsersByCondition" resultType="User">
SELECT id, username, age FROM user
WHERE 1=1
<if test="username != null">AND username LIKE CONCAT('%', #{username}, '%')</if>
<if test="age != null">AND age = #{age}</if>
</select>11. 傳遞 Map 參數(shù)
靈活傳遞多個零散參數(shù),用 #{key} 接收 Map 中的鍵值對:
// 傳遞 Map 作為參數(shù)
List<User> getUsersByMap(Map<String, Object> params);<!-- 使用 Map 的 key 作為參數(shù) -->
<select id="getUsersByMap" resultType="User">
SELECT id, username, age FROM user
WHERE username = #{name} AND age > #{minAge}
</select>// 調(diào)用方式
Map<String, Object> params = new HashMap<>();
params.put("name", "張三");
params.put("minAge", 18);
List<User> users = userMapper.getUsersByMap(params);四、動態(tài) SQL:靈活處理條件查詢
12. if + where 標簽:動態(tài)條件查詢
where 標簽會自動處理多余的 AND/OR,避免 SQL 語法錯誤:
<!-- 動態(tài)條件查詢(自動處理 AND) -->
<select id="getUserList" resultType="User">
SELECT id, username, age FROM user
<where>
<if test="username != null">AND username LIKE #{username}</if>
<if test="age != null">AND age = #{age}</if>
</where>
</select>對比傳統(tǒng) SQL:無需手動拼接 WHERE 1=1,where 標簽會智能判斷是否添加 WHERE 關(guān)鍵字,多余的 AND 會自動刪除。
13. choose + when + otherwise:多條件分支判斷
類似 Java 中的 if-else,只執(zhí)行第一個滿足條件的分支:
<!-- 多條件分支查詢 -->
<select id="getUserByCondition" resultType="User">
SELECT id, username, age FROM user
WHERE 1=1
<choose>
<when test="id != null">AND id = #{id}</when> <!-- 優(yōu)先按 ID 查詢 -->
<when test="username != null">AND username = #{username}</when> <!-- 其次按用戶名查詢 -->
<otherwise>AND age > 18</otherwise> <!-- 都不滿足時的默認條件 -->
</choose>
</select>14. foreach 批量插入
一次性插入多條數(shù)據(jù),比循環(huán)單條插入效率提升 10 倍:
<!-- 批量插入用戶 -->
<insert id="batchInsert">
INSERT INTO user (username, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.username}, #{user.age})
</foreach>
</insert>List<User> users = Arrays.asList(
new User("張三", 20),
new User("李四", 22)
);
userMapper.batchInsert(users); // 一次插入多條數(shù)據(jù)五、結(jié)果映射:解決字段與屬性不一致問題
15. resultMap:自定義字段與屬性映射
當數(shù)據(jù)庫字段與 Java 實體類屬性名稱不一致時,用 resultMap 手動映射:
<!-- 自定義結(jié)果映射(解決字段與屬性名不一致) -->
<resultMap id="userResultMap" type="User">
<id property="userId" column="id"/> <!-- 主鍵映射 -->
<result property="userName" column="username"/> <!-- 普通字段映射 -->
<result property="userAge" column="age"/>
</resultMap>
<select id="getUserById" resultMap="userResultMap">
SELECT id, username, age FROM user WHERE id = #{id}
</select>// 實體類屬性與數(shù)據(jù)庫字段不同
public class User {
private Long userId; // 對應(yīng)數(shù)據(jù)庫 id
private String userName; // 對應(yīng)數(shù)據(jù)庫 username
private Integer userAge; // 對應(yīng)數(shù)據(jù)庫 age
// getter/setter
}16. 一對一關(guān)聯(lián)查詢(如訂單關(guān)聯(lián)用戶)
查詢訂單時同時關(guān)聯(lián)查詢用戶信息,用 association 標簽配置:
<!-- 訂單關(guān)聯(lián)查詢用戶(一對一) -->
<resultMap id="orderResultMap" type="Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<!-- 關(guān)聯(lián)用戶信息 -->
<association property="user" javaType="User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
</association>
</resultMap>
<select id="getOrderById" resultMap="orderResultMap">
SELECT o.id AS order_id, o.order_no, u.id AS user_id, u.username
FROM order o
LEFT JOIN user u ON o.user_id = u.id
WHERE o.id = #{id}
</select>// 訂單實體類包含用戶對象
public class Order {
private Long id;
private String orderNo;
private User user; // 關(guān)聯(lián)的用戶信息
// getter/setter
}17. 一對多關(guān)聯(lián)查詢(如用戶關(guān)聯(lián)訂單列表)
查詢用戶時同時查詢其所有訂單,用 collection 標簽配置:
<!-- 用戶關(guān)聯(lián)查詢訂單列表(一對多) -->
<resultMap id="userOrderResultMap" type="User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<!-- 關(guān)聯(lián)訂單列表 -->
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
</collection>
</resultMap>
<select id="getUserWithOrders" resultMap="userOrderResultMap">
SELECT u.id AS user_id, u.username, o.id AS order_id, o.order_no
FROM user u
LEFT JOIN order o ON u.id = o.user_id
WHERE u.id = #{id}
</select>// 用戶實體類包含訂單列表
public class User {
private Long id;
private String username;
private List<Order> orders; // 關(guān)聯(lián)的訂單列表
// getter/setter
}六、高級配置:提升性能與靈活性
18. 別名配置:簡化類名書寫
在全局配置文件中定義別名,XML 映射文件中直接使用短名稱,避免寫全類名:
<!-- mybatis-config.xml 中配置別名 -->
<typeAliases>
<typeAlias type="com.example.pojo.User" alias="User"/> <!-- 單個類別名 -->
<package name="com.example.pojo"/> <!-- 包下所有類自動取首字母小寫別名 -->
</typeAliases><!-- 直接使用別名 -->
<select id="getUserById" resultType="User"> <!-- 無需寫全類名 -->
SELECT id, username FROM user WHERE id = #{id}
</select>19. 分頁查詢:使用 PageHelper 插件
MyBatis 本身不支持物理分頁,集成 PageHelper 插件實現(xiàn)高效分頁:
<!-- pom.xml 引入依賴 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>// 分頁查詢(PageHelper 自動攔截 SQL 添加分頁條件)
PageHelper.startPage(1, 10); // 第 1 頁,每頁 10 條
List<User> userList = userMapper.getUserList();
Page<User> page = (Page<User>) userList;
System.out.println("總條數(shù):" + page.getTotal());
System.out.println("總頁數(shù):" + page.getPages());20. 緩存配置:一級緩存與二級緩存
MyBatis 自帶緩存機制,減少數(shù)據(jù)庫查詢次數(shù):
- 一級緩存:默認開啟,SqlSession 級別的緩存,同一 SqlSession 內(nèi)查詢相同數(shù)據(jù)會命中緩存。
- 二級緩存:Mapper 級別的緩存,多個 SqlSession 共享,需手動開啟。
<!-- 在 Mapper.xml 中開啟二級緩存 -->
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>
<!-- 禁用某個方法的緩存 -->
<select id="getUserById" useCache="false">
SELECT id, username FROM user WHERE id = #{id}
</select>注意:二級緩存適合查詢頻繁、修改較少的數(shù)據(jù),且實體類需實現(xiàn) Serializable 接口。
21. 類型轉(zhuǎn)換器:處理特殊類型
自定義類型轉(zhuǎn)換器,解決數(shù)據(jù)庫類型與 Java 類型不匹配問題(如數(shù)據(jù)庫 VARCHAR 存儲 JSON 字符串,轉(zhuǎn)換為 Java 對象):
// 自定義類型轉(zhuǎn)換器
public class JsonTypeHandler<T> extends BaseTypeHandler<T> {
private Class<T> type;
// 實現(xiàn) getResult、setParameter 等方法,完成 JSON 與對象的轉(zhuǎn)換
}<!-- 配置類型轉(zhuǎn)換器 -->
<typeHandlers>
<typeHandler handler="com.example.handler.JsonTypeHandler" javaType="com.example.pojo.Info"/>
</typeHandlers>
<!-- 在 SQL 映射中使用 -->
<resultMap id="userResultMap" type="User">
<result property="info" column="info" typeHandler="com.example.handler.JsonTypeHandler"/>
</resultMap>七、實戰(zhàn)技巧:避坑與性能優(yōu)化
22. #{} 與 ${} 的區(qū)別:防止 SQL 注入
#{} 會預(yù)編譯 SQL(參數(shù)用?占位),${} 直接拼接 SQL(有注入風(fēng)險):
<!-- 安全:#{} 預(yù)編譯,參數(shù)作為值傳入 -->
SELECT * FROM user WHERE username = #{username}
<!-- 危險:${} 直接拼接,可能導(dǎo)致 SQL 注入 -->
SELECT * FROM user WHERE username = '${username}'
<!-- 如果 username 為 '張三' OR '1'='1,會查詢所有用戶 -->使用原則:優(yōu)先用 #{},只有在動態(tài)拼接表名、排序字段時才用 ${},且必須嚴格校驗參數(shù)。
23. 批量更新:減少 SQL 執(zhí)行次數(shù)
通過 foreach 拼接批量更新語句,比循環(huán)單條更新效率提升 10 倍:
<!-- 批量更新(MySQL 支持,需開啟 allowMultiQueries=true) -->
<update id="batchUpdate">
<foreach collection="list" item="user" separator=";">
UPDATE user SET username = #{user.username} WHERE id = #{user.id}
</foreach>
</update>注意:MySQL 需在連接 URL 中添加 allowMultiQueries=true 才能支持批量更新。
24. 懶加載:按需加載關(guān)聯(lián)數(shù)據(jù)
關(guān)聯(lián)查詢時默認不加載關(guān)聯(lián)數(shù)據(jù),用到時才查詢,減少不必要的 SQL 執(zhí)行:
<!-- 全局配置開啟懶加載 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 一對一懶加載 -->
<association property="user" javaType="User" select="getUserById" column="user_id"/>// 調(diào)用時只查詢訂單,訪問 user 屬性時才查詢用戶
Order order = orderMapper.getOrderById(1L); // 只執(zhí)行訂單查詢 SQL
User user = order.getUser(); // 此時才執(zhí)行用戶查詢 SQL25. SQL 片段:復(fù)用重復(fù) SQL
將重復(fù)的 SQL 片段抽取為 <sql> 標簽,用 <include> 引用,減少代碼冗余:
<!-- 定義 SQL 片段 -->
<sql id="userColumns">id, username, age</sql>
<!-- 引用 SQL 片段 -->
<select id="getUserById" resultType="User">
SELECT <include refid="userColumns"/> FROM user WHERE id = #{id}
</select>
<select id="getUserList" resultType="User">
SELECT <include refid="userColumns"/> FROM user
</select>為什么掌握這些能讓你少加班?
MyBatis 是 Java 后端開發(fā)的必備技能,但 80% 的開發(fā)者都在重復(fù)踩坑:SQL 注入、關(guān)聯(lián)查詢混亂、動態(tài) SQL 拼接錯誤、性能低下…… 這 25 個案例覆蓋了 90% 的實際開發(fā)場景,從基礎(chǔ)配置到高級優(yōu)化,讓你:
- 寫出更安全的 SQL(避免注入)
- 減少 50% 的代碼量(接口綁定 + 動態(tài) SQL)
- 提升查詢性能(緩存 + 批量操作 + 懶加載)






















