從入門到精通詳解SpringBoot整合MyBatis的全攻略
一、初識 MyBatis:為什么它如此受歡迎?
在 Java 持久層框架的版圖中,MyBatis 無疑占據著舉足輕重的地位。它是一個優(yōu)秀的半自動 ORM(對象關系映射)框架,能夠將 Java 對象與數據庫記錄進行映射,支持自定義 SQL、存儲過程以及高級映射。相比于 Hibernate 的全自動 ORM,MyBatis 允許開發(fā)者直接編寫 SQL,在需要精細控制 SQL 執(zhí)行和復雜查詢的場景中,展現出無可比擬的靈活性和性能優(yōu)勢。
簡單來說,MyBatis 的核心工作就是 將 SQL 語句與 Java 代碼解耦,通過 XML 或注解的配置方式,讓你既能享受框架帶來的便捷,又能牢牢掌控 SQL 的每一個細節(jié)。正因如此,MyBatis 在國內的 Java 開發(fā)社區(qū)中堪稱“半壁江山”,幾乎是企業(yè)級項目數據訪問層的標準配置。
1.1 MyBatis 的核心優(yōu)勢
SQL 與代碼解耦:MyBatis 通過 XML 或注解方式將 SQL 語句與 Java 代碼分離,開發(fā)者可獨立維護數據庫操作邏輯。DBA 可以直接優(yōu)化 SQL 而無需修改業(yè)務代碼,這在需要精細控制 SQL 的遺留系統(tǒng)遷移場景中尤為珍貴。
動態(tài) SQL 靈活構建:MyBatis 提供 <if>、<choose>、<foreach> 等標簽實現條件拼接,相比于 JPA 的 Criteria API,MyBatis 的動態(tài) SQL 更接近原生 SQL 語法,調試成本可降低 60% 以上。
性能優(yōu)化空間巨大:一級緩存默認開啟,可減少重復查詢,緩存命中率可達 35%;延遲加載通過 fetchType="lazy" 實現關聯對象按需加載,避免 N+1 查詢問題;批處理模式下,1000 條插入操作可從 10 秒壓縮至 2 秒。
數據庫兼容性強:支持 Oracle、MySQL、PostgreSQL 等 12 種主流數據庫,通過 typeAlias 和 typeHandlers 可無縫適配特殊數據類型。
1.2 MyBatis 的局限性
當然,任何框架都不是萬能的。當實體類包含 50+ 字段時,XML 映射文件容易變得臃腫(單個實體映射文件超過 800 行時,字段修改導致的維護錯誤率可能上升 40%)。此外,分頁需要手動處理,相比 MyBatis-Plus 的自動分頁略顯繁瑣。
二、MyBatis 核心架構解析
2.1 核心組件
MyBatis 的核心組件如同一個精密的機器,各司其職:
| 組件 | 作用 |
|---|---|
SqlSessionFactoryBuilder | 使用建造者模式,解析配置文件并創(chuàng)建 SqlSessionFactory |
SqlSessionFactory | 工廠類,用于創(chuàng)建 SqlSession,應設計為單例 |
SqlSession | 代表一次數據庫會話,提供增刪改查 API,線程不安全 |
Configuration | 存儲全局配置信息、映射信息、類型處理器,是 MyBatis 的“大腦” |
Executor | 執(zhí)行器,負責執(zhí)行 SQL,維護一級緩存 |
StatementHandler | 封裝 JDBC Statement 的操作 |
ParameterHandler | 負責處理 SQL 參數 |
ResultSetHandler | 負責結果集的映射 |
2.2 一條 SQL 的執(zhí)行全流程
以執(zhí)行一次 userMapper.selectUserById(1) 為例,背后的故事是這樣的:
1. 初始化
├─ SqlSessionFactoryBuilder → 構建 SqlSessionFactory
└─ SqlSessionFactory → 解析 XML/注解,構建 Configuration
2. 獲取 SqlSession
└─ SqlSessionFactory.openSession() → 創(chuàng)建 DefaultSqlSession
3. Mapper 動態(tài)代理
└─ UserMapper mapper = sqlSession.getMapper(UserMapper.class)(生成 MapperProxy 代理對象)
4. 執(zhí)行 SQL
├─ mapper.selectUserById(1)
├─ MapperProxy.invoke() → 根據方法找到 MappedStatement
└─ SqlSession.selectOne(mappedStatement, params)
5. Executor 執(zhí)行
├─ CachingExecutor (二級緩存) → delegate → SimpleExecutor
├─ 創(chuàng)建 StatementHandler
├─ ParameterHandler 設置參數
├─ JDBC PreparedStatement 執(zhí)行 SQL
└─ ResultSetHandler 映射結果
6. 返回結果
整個流程的核心在于:配置 → SqlSessionFactory → SqlSession → Executor → MappedStatement → 數據庫。
2.3 MappedStatement 的秘密
MappedStatement 是 MyBatis 中一個關鍵的底層封裝對象,它包裝了 MyBatis 配置信息及 SQL 映射信息。mapper.xml 文件中的一個 SQL 對應一個 MappedStatement 對象,SQL 的 id 就是 MappedStatement 的 id。在 SQL 執(zhí)行前,Executor 通過 MappedStatement 將輸入的 Java 對象映射至 SQL 中;執(zhí)行后,再將輸出結果映射至 Java 對象中。
三、Spring Boot 整合 MyBatis(實戰(zhàn)篇)
3.1 依賴導入
在 Spring Boot 項目中整合 MyBatis,首先需要在 pom.xml 中添加相關依賴:
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.2</version>
</dependency>
<!-- MySQL 驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 可選:連接池(HikariCP 已默認包含) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>小貼士:mybatis-spring-boot-starter 會自動引入 mybatis 和 mybatis-spring 依賴,我們只需添加這一個 starter 即可。
3.2 配置文件詳解(application.yml)
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10 # 最大連接池數
minimum-idle: 5 # 最小空閑連接
connection-timeout: 30000 # 連接超時時間
idle-timeout: 600000 # 空閑超時時間
max-lifetime: 1800000 # 連接最大生命周期
mybatis:
# 實體類包路徑,用于別名自動注冊
type-aliases-package: com.example.demo.entity
# Mapper XML 文件位置
mapper-locations: classpath:mapper/*.xml
configuration:
# 開啟駝峰命名自動映射(例如:user_name → userName)
map-underscore-to-camel-case: true
# 開啟二級緩存
cache-enabled: true
# 開啟延遲加載
lazy-loading-enabled: true
# 日志輸出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl關鍵配置解讀:
map-underscore-to-camel-case: true:這個配置非常實用!開啟后,數據庫字段user_name會自動映射到 Java 實體類的userName屬性,省去了大量手動映射的煩惱。type-aliases-package:指定實體類包路徑后,在 Mapper XML 中就可以直接使用類名(小寫形式)而非全限定名。mapper-locations:指定 XML 映射文件的存放位置。
3.3 基于 XML 的開發(fā)方式
這是 MyBatis 最經典的開發(fā)模式,適合復雜的 SQL 映射場景。
Step 1:創(chuàng)建實體類
package com.example.demo.entity;
import lombok.Data;
import java.util.Date;
@Data
public class User {
private Long id;
private String userName;
private String password;
private String email;
private Integer age;
private Integer status;
private Date createTime;
private Date updateTime;
}
Step 2:創(chuàng)建 Mapper 接口
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface UserMapper {
User selectUserById(@Param("id") Long id);
List<User> selectUserList(@Param("userName") String userName);
int insertUser(User user);
int updateUser(User user);
int deleteUserById(@Param("id") Long id);
}
Step 3:編寫 Mapper XML 文件
在 src/main/resources/mapper/ 目錄下創(chuàng)建 UserMapper.xml:
<?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.example.demo.mapper.UserMapper">
<!-- 結果映射 -->
<resultMap id="BaseResultMap" type="User">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="user_name" property="userName" jdbcType="VARCHAR"/>
<result column="password" property="password" jdbcType="VARCHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
<result column="status" property="status" jdbcType="INTEGER"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 基礎字段列表 -->
<sql id="Base_Column_List">
id, user_name, password, email, age, status, create_time, update_time
</sql>
<!-- 根據 ID 查詢用戶 -->
<select id="selectUserById" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM user
WHERE id = #{id}
</select>
<!-- 條件查詢用戶列表 -->
<select id="selectUserList" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM user
WHERE 1=1
<if test="userName != null and userName != ''">
AND user_name LIKE CONCAT('%', #{userName}, '%')
</if>
ORDER BY create_time DESC
</select>
<!-- 插入用戶 -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (user_name, password, email, age, status, create_time, update_time)
VALUES (#{userName}, #{password}, #{email}, #{age}, #{status}, NOW(), NOW())
</insert>
<!-- 更新用戶 -->
<update id="updateUser">
UPDATE user
<set>
<if test="userName != null">user_name = #{userName},</if>
<if test="password != null">password = #{password},</if>
<if test="email != null">email = #{email},</if>
<if test="age != null">age = #{age},</if>
<if test="status != null">status = #{status},</if>
update_time = NOW()
</set>
WHERE id = #{id}
</update>
<!-- 刪除用戶 -->
<delete id="deleteUserById">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>Step 4:創(chuàng)建 Service 層
package com.example.demo.service;
import com.example.demo.entity.User;
import java.util.List;
public interface UserService {
User getUserById(Long id);
List<User> getUserList(String userName);
int addUser(User user);
int modifyUser(User user);
int removeUser(Long id);
}
package com.example.demo.service.impl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Long id) {
return userMapper.selectUserById(id);
}
@Override
public List<User> getUserList(String userName) {
return userMapper.selectUserList(userName);
}
@Override
public int addUser(User user) {
return userMapper.insertUser(user);
}
@Override
public int modifyUser(User user) {
return userMapper.updateUser(user);
}
@Override
public int removeUser(Long id) {
return userMapper.deleteUserById(id);
}
}
Step 5:Controller 層測試
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@GetMapping("/list")
public List<User> getUserList(@RequestParam(required = false) String userName) {
return userService.getUserList(userName);
}
@PostMapping
public int addUser(@RequestBody User user) {
return userService.addUser(user);
}
@PutMapping
public int updateUser(@RequestBody User user) {
return userService.modifyUser(user);
}
@DeleteMapping("/{id}")
public int deleteUser(@PathVariable Long id) {
return userService.removeUser(id);
}
}
3.4 基于注解的開發(fā)方式
對于簡單的 CRUD 操作,注解方式更加簡潔高效:
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserAnnotationMapper {
@Select("SELECT id, user_name, password, email, age, status, create_time, update_time FROM user WHERE id = #{id}")
@Results(id = "userResultMap", value = {
@Result(column = "id", property = "id", id = true),
@Result(column = "user_name", property = "userName"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
User selectUserById(@Param("id") Long id);
@SelectProvider(type = UserSqlProvider.class, method = "selectUserList")
@ResultMap("userResultMap")
List<User> selectUserList(@Param("userName") String userName);
@Insert("INSERT INTO user (user_name, password, email, age, status, create_time, update_time) " +
"VALUES (#{userName}, #{password}, #{email}, #{age}, #{status}, NOW(), NOW())")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUser(User user);
@Update("UPDATE user SET user_name = #{userName}, password = #{password}, email = #{email}, " +
"age = #{age}, status = #{status}, update_time = NOW() WHERE id = #{id}")
int updateUser(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteUserById(@Param("id") Long id);
}
對于復雜查詢,可以使用 @SelectProvider 配合 SQL 構建器實現動態(tài) SQL:
package com.example.demo.mapper;
import org.apache.ibatis.jdbc.SQL;
public class UserSqlProvider {
public String selectUserList(String userName) {
return new SQL() {{
SELECT("id, user_name, password, email, age, status, create_time, update_time");
FROM("user");
if (userName != null && !userName.isEmpty()) {
WHERE("user_name LIKE CONCAT('%', #{userName}, '%')");
}
ORDER_BY("create_time DESC");
}}.toString();
}
}
3.5 Mapper 掃描配置
除了在每一個 Mapper 接口上添加 @Mapper 注解,還可以通過 @MapperScan 在配置類中批量掃描:
package com.example.demo.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.example.demo.mapper") // 掃描整個 mapper 包
public class MyBatisConfig {
}
四、MyBatis 核心功能深入
4.1 參數傳遞方式
在 MyBatis 中,Mapper 接口方法支持多種參數傳遞方式:
方式一:使用 @Param 注解(推薦)
User selectUser(@Param("id") Long id, @Param("status") Integer status);
<select id="selectUser" resultType="User">
SELECT * FROM user WHERE id = #{id} AND status = #{status}
</select>方式二:使用對象參數
int insertUser(User user);
<insert id="insertUser">
INSERT INTO user (user_name, password) VALUES (#{userName}, #{password})
</insert>方式三:使用 Map 參數
List<User> selectUserByMap(Map<String, Object> params);
4.2 結果集映射
MyBatis 提供了兩種結果集映射方式:
簡單映射(resultType) :當查詢結果的列名與 Java 對象屬性名一致(或開啟駝峰映射后能匹配)時使用。
復雜映射(resultMap) :支持一對一、一對多等復雜關系映射:
- 一對一(association) :適用于主表與從表是一對一關系(如:用戶和用戶詳情)
- 一對多(collection) :適用于主表與從表是一對多關系(如:用戶和訂單列表)
<!-- 一對一關聯映射 -->
<resultMap id="UserWithDetailResultMap" type="User" extends="BaseResultMap">
<association property="userDetail" javaType="UserDetail" column="id"
select="com.example.demo.mapper.UserDetailMapper.selectByUserId"/>
</resultMap>
<!-- 一對多關聯映射 -->
<resultMap id="UserWithOrdersResultMap" type="User" extends="BaseResultMap">
<collection property="orderList" ofType="Order" column="id"
select="com.example.demo.mapper.OrderMapper.selectByUserId"/>
</resultMap>性能提示:對于關聯映射,建議開啟延遲加載(lazyLoadingEnabled=true),避免 N+1 查詢問題。
4.3 動態(tài) SQL(核心精華)
動態(tài) SQL 是 MyBatis 最強大的特性之一,允許根據運行時條件動態(tài)生成 SQL 語句。
常用動態(tài) SQL 標簽詳解
| 標簽 | 作用 | 示例 |
|---|---|---|
<if> | 條件判斷,滿足條件時包含 SQL 片段 | <if test="name != null">AND name = #{name}</if> |
<choose> | 多條件分支(類似 switch-case) | <choose><when test="age != null">AND age = #{age}</when><otherwise>AND age > 18</otherwise></choose> |
<where> | 智能處理 WHERE 子句,自動去除開頭 AND/OR | <where><if test="name != null">AND name = #{name}</if></where> |
<set> | 智能處理 UPDATE 語句,自動去除結尾逗號 | <set><if test="name != null">name = #{name},</if></set> |
<foreach> | 遍歷集合生成批量操作 | <foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach> |
<trim> | 自定義字符串修剪 | <trim prefix="WHERE" prefixOverrides="AND">...</trim> |
<bind> | 創(chuàng)建變量并綁定到上下文 | <bind name="pattern" value="'%' + keyword + '%'" /> |
批量插入示例
<insert id="batchInsertUsers">
INSERT INTO user (user_name, password, email) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.userName}, #{user.password}, #{user.email})
</foreach>
</insert>批量刪除示例
<delete id="batchDeleteUsers">
DELETE FROM user WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>動態(tài) SQL 最佳實踐:避免嵌套超過 3 層,使用 <where>、<set> 減少無效字符處理開銷,參數合法性在 Java 層校驗,簡單場景可用 @SelectProvider 替代 XML。
4.4 緩存機制
MyBatis 提供了兩級緩存來提升查詢性能:
一級緩存(Local Cache)
- 作用域:SqlSession 級別,默認開啟且無法關閉
- 生命周期:與 SqlSession 一致,執(zhí)行 INSERT/UPDATE/DELETE 操作或調用
commit()、close()時會清空 - 適用場景:同一會話內的重復查詢,緩存命中率約 5%
二級緩存(Global Cache)
- 作用域:Mapper 級別(跨 SqlSession 共享),需手動配置開啟
- 存儲介質:默認 JVM 內存,可集成 Redis、Ehcache 等
- 適用場景:多用戶并發(fā)訪問、查詢頻率高且更新不頻繁的數據,緩存命中率可達 90%
啟用二級緩存配置:
# application.yml
mybatis:
configuration:
cache-enabled: true # 全局開啟二級緩存<!-- Mapper XML 中添加 cache 標簽 -->
<mapper namespace="com.example.demo.mapper.UserMapper">
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="false"/>
</mapper>// 或者使用注解方式
@CacheNamespace(eviction = LruCache.class, flushInterval = 60000, size = 512)
public interface UserMapper {
}
五、分頁方案
5.1 PageHelper 插件(推薦)
PageHelper 是目前最流行的 MyBatis 分頁插件,只需在查詢前調用 PageHelper.startPage() 即可自動實現物理分頁。
添加依賴:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>使用示例:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public PageInfo<User> getUserListByPage(int pageNum, int pageSize, String userName) {
// 開啟分頁(緊跟在后面的第一個查詢會被分頁)
PageHelper.startPage(pageNum, pageSize);
// 執(zhí)行查詢
List<User> userList = userMapper.selectUserList(userName);
// 封裝分頁結果
return new PageInfo<>(userList);
}
}
Controller 調用:
@GetMapping("/page")
public PageInfo<User> getUserByPage(@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(required = false) String userName) {
return userService.getUserListByPage(pageNum, pageSize, userName);
}
5.2 分頁實現方式對比
MyBatis 主要有五種分頁實現方式:
| 方式 | 類型 | 原理 | 適用場景 | 性能 |
|---|---|---|---|---|
| RowBounds | 邏輯分頁 | 查詢全部結果,內存中截取 | 小數據量 | 大數據量易內存溢出 |
| PageHelper | 物理分頁 | 攔截器動態(tài)添加 LIMIT 子句 | 通用推薦 | 優(yōu)秀 |
| SQL 分頁 | 物理分頁 | 手動編寫 LIMIT/OFFSET | 簡單場景 | 較好 |
| 數組分頁 | 邏輯分頁 | 查全表后 subList 截取 | 小數據量 | 差 |
| 攔截器分頁 | 物理分頁 | 自定義攔截器統(tǒng)一處理 | 需要統(tǒng)一分頁規(guī)范時 | 優(yōu)秀 |
最佳實踐:推薦優(yōu)先使用 PageHelper 等物理分頁方式,避免深分頁陷阱。對于超大數據量場景,建議設置 count=false 跳過統(tǒng)計總記錄數以提升性能。
六、性能優(yōu)化指南
6.1 SQL 語句優(yōu)化
- 精準查詢字段:避免使用
SELECT *,只查詢業(yè)務所需字段 - 使用
<sql>標簽:定義可復用字段集合,減少重復書寫 - 批量操作合并:使用
<foreach>生成批量插入/更新語句,減少數據庫交互次數 - 避免 N+1 查詢:合理使用延遲加載或聯表查詢
6.2 緩存策略優(yōu)化
- 一級緩存:默認開啟,需配合
@Transactional注解生效,適用于單事務內重復查詢場景 - 二級緩存:需手動配置開啟,適合多用戶并發(fā)訪問場景。注意:不推薦跨 Mapper 共享熱點數據,易引發(fā)緩存雪崩
- 分布式緩存:在二級緩存基礎上集成 Redis,通過
@Cacheable注解實現跨服務緩存一致性
6.3 連接池與監(jiān)控
- 連接池推薦:HikariCP(Spring Boot 默認)或 Druid,根據業(yè)務負載調整
maximum-pool-size、connection-timeout等參數 - 監(jiān)控工具:通過 Druid 監(jiān)控 SQL 執(zhí)行時間,識別慢查詢
- 日志控制:生產環(huán)境關閉 MyBatis 詳細日志(
log4j.logger.org.mybatis=ERROR),減少 IO 開銷
七、多數據源配置
在微服務或復雜業(yè)務場景中,多數據源的需求十分常見。推薦使用 dynamic-datasource-spring-boot-starter 開源組件來優(yōu)雅地實現。
7.1 添加依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>7.2 配置文件
spring:
datasource:
dynamic:
primary: master # 默認數據源
strict: false # 嚴格模式(匹配不到時拋異常)
datasource:
master:
url: jdbc:mysql://localhost:3306/master_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver7.3 使用 @DS 注解切換數據源
@Service
@DS("master") // 類級別指定默認數據源
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@DS("slave") // 方法級別覆蓋,從從庫讀取
public User getUserById(Long id) {
return userMapper.selectUserById(id);
}
@Override
@DS("master") // 寫入操作使用主庫
public int addUser(User user) {
return userMapper.insertUser(user);
}
}
注解優(yōu)先順序:方法級別 > 類級別,dynamic-datasource 還支持讀寫分離、敏感信息加密、分布式事務(基于 Seata)等高級特性。
八、MyBatis-Plus:MyBatis 的超強增強工具
MyBatis-Plus(簡稱 MP)是 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,極大簡化了單表 CRUD 操作。
8.1 核心特性
- 無侵入:只做增強,不改動 MyBatis 原有邏輯
- 內置通用 Mapper:提供
BaseMapper接口,包含常用 CRUD 方法,無需編寫 XML - 條件構造器:提供
LambdaQueryWrapper,類型安全的條件構造體驗 - 代碼生成器:自動生成 Entity、Mapper、Service、Controller 全套代碼
- 內置分頁插件:基于物理分頁,支持多種數據庫
8.2 代碼生成器
MyBatis-Plus 的代碼生成器 AutoGenerator 可以根據數據庫表結構自動生成整套代碼,極大提升開發(fā)效率。
添加依賴:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>編寫代碼生成器主類:
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_demo", "root", "123456")
.globalConfig(builder -> {
builder.author("your_name")
.outputDir(System.getProperty("user.dir") + "/src/main/java")
.commentDate("yyyy/MM/dd");
})
.packageConfig(builder -> {
builder.parent("com.example.demo")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller");
})
.strategyConfig(builder -> {
builder.addInclude("user") // 指定要生成的表名
.entityBuilder()
.enableLombok() // 開啟 Lombok 支持
.idType(IdType.AUTO); // 主鍵策略
})
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}
九、常見問題與解決方案
9.1 駝峰命名映射問題
如果開啟了 map-underscore-to-camel-case 后仍映射失敗,檢查:
- 實體類屬性是否使用了 Lombok(確保有 getter/setter)
- 數據庫字段名與屬性名是否真的匹配(如
userNamevsuser_name)
9.2 SQL 注入問題
始終使用 #{}(預編譯參數)而不是 ${}(字符串拼接)。${} 僅用于無法預編譯的場景,如動態(tài)表名、列名。務必對用戶輸入進行嚴格校驗。
9.3 分頁失效問題
如果 PageHelper 分頁不生效,檢查:
PageHelper.startPage()后是否立即執(zhí)行了查詢- 分頁參數是否被正確傳遞
- 是否有其他攔截器干擾
9.4 延遲加載不生效
確保在配置文件中開啟 lazyLoadingEnabled=true,且關聯查詢使用 select 屬性指定子查詢。
十、總結與展望
本文從 MyBatis 的核心概念、架構原理入手,詳細講解了 Spring Boot 整合 MyBatis 的兩種主流方式(XML 與注解),深入剖析了動態(tài) SQL、緩存機制、分頁方案、性能優(yōu)化、多數據源配置等進階內容,最后介紹了 MyBatis-Plus 這個強大的增強工具。
在實際項目開發(fā)中,建議根據場景選擇合適的組合:
- 簡單項目:Spring Boot + MyBatis(注解方式)+ PageHelper
- 復雜項目:Spring Boot + MyBatis(XML 方式)+ MyBatis-Plus(僅使用增強功能)
- 極速開發(fā):Spring Boot + MyBatis-Plus + 代碼生成器
MyBatis 以其靈活性和高性能,在國內 Java 社區(qū)擁有極其龐大的用戶基礎。掌握 MyBatis,不僅是一項必備的 Java 開發(fā)技能,更是通向高級工程師道路上的一塊重要基石。希望本文能幫助你全面掌握 MyBatis 的核心知識,在實際項目中游刃有余!
以上就是從入門到精通詳解SpringBoot整合MyBatis的全攻略的詳細內容,更多關于SpringBoot整合MyBatis的資料請關注腳本之家其它相關文章!
相關文章
SpringBoot開發(fā)實戰(zhàn)系列之動態(tài)定時任務
在我們日常的開發(fā)中,很多時候,定時任務都不是寫死的,而是寫到數據庫中,從而實現定時任務的動態(tài)配置,下面這篇文章主要給大家介紹了關于SpringBoot開發(fā)實戰(zhàn)系列之動態(tài)定時任務的相關資料,需要的朋友可以參考下2021-08-08

