MyBatis?Mapper?接口是如何找到并執(zhí)行SQL的
每一位使用 MyBatis 的 Java 開發(fā)者,都曾體驗過這種“魔法”:我們只定義了一個簡單的 UserMapper 接口,沒有寫任何實現(xiàn)類,但只要在 Service 中注入它并調(diào)用其方法,數(shù)據(jù)庫操作就奇跡般地完成了。
// 在 Service 中
@Autowired
private UserMapper userMapper;
public User getUser(Long id) {
// 只是調(diào)用了一個接口方法
return userMapper.selectById(id);
}UserMapper 只是一個接口,它沒有實現(xiàn)類,那 userMapper 這個注入的 Bean 到底是什么?selectById 這個方法又是如何與具體的 SQL 語句關(guān)聯(lián)起來的呢?
本文將為你揭開 MyBatis 這層神秘的面紗,深入剖析其接口與 SQL 的映射原理,并總結(jié)在 Spring Boot 環(huán)境下的最佳實踐和常見陷阱。
1. 核心原理:JDK 動態(tài)代理 (Dynamic Proxy)
MyBatis 的“魔法”核心,正是 Java 的 JDK 動態(tài)代理 技術(shù)。
當(dāng)你試圖從 Spring 容器中獲取一個 UserMapper 實例時,Spring 實際上是請求 MyBatis 的 MapperFactoryBean 來創(chuàng)建一個 Bean。MyBatis 并不會去尋找這個接口的實現(xiàn)類,而是利用 JDK 的 Proxy 類,在運行時動態(tài)地為你創(chuàng)建一個該接口的代理對象。
這個代理對象,就是你注入到 Service 中的 userMapper。它具備了 UserMapper 接口的所有方法,但它不是一個普通的實現(xiàn)。你可以把它想象成一個聰明的“中介”或“調(diào)度員”。
當(dāng)你調(diào)用 userMapper.selectById(1L) 時,實際發(fā)生了以下事情:
- 1. 方法攔截: 這個調(diào)用首先被代理對象攔截。
- 2. 信息解析: 代理對象會解析出你調(diào)用的信息,包括:
- • 接口名:
com.example.mapper.UserMapper - • 方法名:
selectById - • 參數(shù):
1L
- • 接口名:
- 3. SQL 尋址: 這是最關(guān)鍵的一步。代理對象會根據(jù)**“接口名 + 方法名”**這個唯一的坐標(biāo),去 MyBatis 的全局配置中尋找與之對應(yīng)的 SQL 語句。
- 4. SQL 執(zhí)行: 找到 SQL 后,代理對象會利用底層的
SqlSession,將參數(shù)1L傳遞進(jìn)去,通過 JDBC 執(zhí)行這條 SQL。 - 5. 結(jié)果映射: 將數(shù)據(jù)庫返回的結(jié)果,根據(jù)配置映射成 Java 對象并返回。
所以,整個問題的關(guān)鍵就變成了:MyBatis 是如何完成第3步——**“SQL 尋址”**的?
2. “尋址”的藝術(shù):兩種主要的映射方式
MyBatis 提供了兩種方式,來告訴代理對象“接口方法”和“SQL語句”之間的對應(yīng)關(guān)系。
方式一:XML 映射文件 (最強(qiáng)大、最常用)
這是 MyBatis 最傳統(tǒng)也是功能最豐富的映射方式。它遵循兩條黃金法則:
法則1:namespace 必須是 Mapper 接口的全限定名。
這是連接 XML 文件和 Java 接口的“紅線”。
法則2:select/insert/update/delete 等標(biāo)簽的 id 必須與接口中的方法名完全一致。
這是定位到具體 SQL 語句的“門牌號”。
示例:
UserMapper.java (接口定義)
package com.example.mapper;
public interface UserMapper {
User selectById(Long id);
void insert(User user);
}UserMapper.xml (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.example.mapper.UserMapper">
<select id="selectById" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="insert">
INSERT INTO users(username, email) VALUES(#{username}, #{email})
</insert>
</mapper>當(dāng)調(diào)用 UserMapper.selectById() 時,代理對象會精確地找到 namespace 為 com.example.mapper.UserMapper 的 XML 文件中,id 為 selectById 的 <select> 標(biāo)簽,并執(zhí)行其中的 SQL。
方式二:注解 (簡單 SQL 的便捷之選)
對于簡單的 SQL 語句,我們可以完全拋棄 XML,直接在接口方法上使用注解。
示例:
package com.example.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(Long id);
@Insert("INSERT INTO users(username, email) VALUES(#{username}, #{email})")
void insert(User user);
}這種方式非常直觀,但當(dāng) SQL 變得復(fù)雜,特別是需要動態(tài) SQL(if, foreach等)時,XML 的表現(xiàn)力遠(yuǎn)勝于注解。
3. 讓 MyBatis 找到你的 Mapper:掃描與注冊
我們已經(jīng)定義好了映射關(guān)系,但還需要告訴 MyBatis 去哪里“發(fā)現(xiàn)”這些接口和 XML 文件。
在現(xiàn)代 Spring Boot 項目中,這通常通過 mybatis-spring-boot-starter 自動完成,我們只需提供少量配置。
1. @MapperScan 注解 (推薦)
在你的 Spring Boot 主啟動類上,添加 @MapperScan 注解,并指定 Mapper 接口所在的包路徑。
@SpringBootApplication
@MapperScan("com.example.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}這個注解會告訴 MyBatis:“請掃描 com.example.mapper 包下的所有接口,并將它們注冊為 Mapper Bean。”
2. @Mapper 注解
你也可以不在啟動類上使用 @MapperScan,而是在每個 Mapper 接口上單獨添加 @Mapper 注解。
@Mapper
public interface UserMapper { ... }這樣,只要 UserMapper 接口在 Spring Boot 的組件掃描路徑下,它就會被自動發(fā)現(xiàn)。但當(dāng) Mapper 接口很多時,@MapperScan 顯然更方便。
3. XML 文件的位置
默認(rèn)情況下,MyBatis Spring Boot Starter 會在 classpath 中,尋找與 Mapper 接口同包同名的 XML 文件。
標(biāo)準(zhǔn)的 Maven 項目結(jié)構(gòu):
src/
main/
java/
com/
example/
mapper/
UserMapper.java
resources/
com/
example/
mapper/
UserMapper.xml <-- XML 放在 resources 目錄下,但包結(jié)構(gòu)與 Java 接口保持一致如果你想把 XML 文件集中存放在另一個地方,可以在 application.yml 中通過 mybatis.mapper-locations 屬性來指定:
mybatis: mapper-locations: classpath:mappers/*.xml
4. 常見問題與陷阱
BindingException: Type ... is not known to the MapperRegistry.
含義: “未在 Mapper 注冊表中找到該類型”。
原因: MyBatis 根本沒發(fā)現(xiàn)你的 Mapper 接口。請檢查 @MapperScan 的包路徑是否正確,或者接口上是否漏了 @Mapper 注解。
BindingException: Invalid bound statement (not found): ...
含義: “無效的綁定語句(未找到)”。
原因: 接口找到了,但接口里的方法沒找到對應(yīng)的 SQL。請檢查:
1. XML 的 namespace 是否是接口的全限定名,一個字母都不能錯。
2. XML 標(biāo)簽的 id 是否與方法名完全一致。
3. XML 文件是否被構(gòu)建工具(Maven/Gradle)正確地打包進(jìn)了最終的 jar 文件中。
XML 文件未被打包:
如果你的 XML 文件放在 src/main/java 目錄下,Maven 默認(rèn)不會打包 .xml 文件。你需要在 pom.xml 中添加如下配置:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>總結(jié)
MyBatis 的接口映射機(jī)制,看似“神奇”,實則建立在一套清晰、嚴(yán)謹(jǐn)?shù)囊?guī)則之上:
- 1. 核心是 JDK 動態(tài)代理,它在運行時為你的接口創(chuàng)建了一個實現(xiàn)類。
- 2. 映射的“鑰匙”是“全限定接口名 + 方法名”。
- 3. 映射關(guān)系可以通過 XML 或注解來定義。
- 4.
@MapperScan是告知 MyBatis 從哪里開始尋找這些“鑰匙”的入口。
到此這篇關(guān)于MyBatis Mapper 接口是如何找到并執(zhí)行 SQL 的的文章就介紹到這了,更多相關(guān)MyBatis Mapper 接口執(zhí)行 SQL內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- mybatis中點擊mapper接口快速定位到對應(yīng)xml中sql方式
- 解決MyBatis Mapper的XML文件SQL語句無法自動提示問題(親測有效)
- MyBatis的通俗理解:SqlSession.getMapper()源碼解讀
- mybatis?mapper.xml中如何根據(jù)數(shù)據(jù)庫類型選擇對應(yīng)SQL語句
- Mybatis?sqlMapConfig.xml中的mappers標(biāo)簽使用
- SpringBoot+Mybatis實現(xiàn)Mapper接口與Sql綁定幾種姿勢
- MyBatis直接執(zhí)行SQL的工具SqlMapper
- Mybatis中SqlMapper配置的擴(kuò)展與應(yīng)用詳細(xì)介紹(1)
相關(guān)文章
JDK安裝方法和Linux常見設(shè)置詳細(xì)版教程
這篇文章主要給大家介紹了關(guān)于JDK安裝方法和Linux常見設(shè)置的相關(guān)資料,文章詳細(xì)介紹了如何在Linux系統(tǒng)中設(shè)置靜態(tài)IP、用戶名和主機(jī)名,配置防火墻,安裝JDK以及如何創(chuàng)建系統(tǒng)快照,需要的朋友可以參考下2024-11-11
關(guān)于@PostConstruct、afterPropertiesSet和init-method的執(zhí)行順序
這篇文章主要介紹了關(guān)于@PostConstruct、afterPropertiesSet和init-method的執(zhí)行順序,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringCloud Admin實戰(zhàn)之健康檢查與全鏈路告警深度解讀
這篇文章主要介紹了SpringCloud Admin實戰(zhàn)之健康檢查與全鏈路告警,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-05-05
rabbitmq basicReject/basicNack/basicRecover的區(qū)別及說明
這篇文章主要介紹了rabbitmq basicReject/basicNack/basicRecover的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
springboot 2.0 mybatis mapper-locations掃描多個路徑的實現(xiàn)
這篇文章主要介紹了springboot 2.0 mybatis mapper-locations掃描多個路徑的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
30分鐘入門Java8之lambda表達(dá)式學(xué)習(xí)
本篇文章主要介紹了30分鐘入門Java8之lambda表達(dá)式學(xué)習(xí),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04

