MyBatis?Mapper?接口是如何找到并執(zhí)行SQL的
每一位使用 MyBatis 的 Java 開發(fā)者,都曾體驗(yàn)過這種“魔法”:我們只定義了一個(gè)簡(jiǎn)單的 UserMapper 接口,沒有寫任何實(shí)現(xiàn)類,但只要在 Service 中注入它并調(diào)用其方法,數(shù)據(jù)庫(kù)操作就奇跡般地完成了。
// 在 Service 中
@Autowired
private UserMapper userMapper;
public User getUser(Long id) {
// 只是調(diào)用了一個(gè)接口方法
return userMapper.selectById(id);
}UserMapper 只是一個(gè)接口,它沒有實(shí)現(xiàn)類,那 userMapper 這個(gè)注入的 Bean 到底是什么?selectById 這個(gè)方法又是如何與具體的 SQL 語(yǔ)句關(guān)聯(lián)起來的呢?
本文將為你揭開 MyBatis 這層神秘的面紗,深入剖析其接口與 SQL 的映射原理,并總結(jié)在 Spring Boot 環(huán)境下的最佳實(shí)踐和常見陷阱。
1. 核心原理:JDK 動(dòng)態(tài)代理 (Dynamic Proxy)
MyBatis 的“魔法”核心,正是 Java 的 JDK 動(dòng)態(tài)代理 技術(shù)。
當(dāng)你試圖從 Spring 容器中獲取一個(gè) UserMapper 實(shí)例時(shí),Spring 實(shí)際上是請(qǐng)求 MyBatis 的 MapperFactoryBean 來創(chuàng)建一個(gè) Bean。MyBatis 并不會(huì)去尋找這個(gè)接口的實(shí)現(xiàn)類,而是利用 JDK 的 Proxy 類,在運(yùn)行時(shí)動(dòng)態(tài)地為你創(chuàng)建一個(gè)該接口的代理對(duì)象。
這個(gè)代理對(duì)象,就是你注入到 Service 中的 userMapper。它具備了 UserMapper 接口的所有方法,但它不是一個(gè)普通的實(shí)現(xiàn)。你可以把它想象成一個(gè)聰明的“中介”或“調(diào)度員”。
當(dāng)你調(diào)用 userMapper.selectById(1L) 時(shí),實(shí)際發(fā)生了以下事情:
- 1. 方法攔截: 這個(gè)調(diào)用首先被代理對(duì)象攔截。
- 2. 信息解析: 代理對(duì)象會(huì)解析出你調(diào)用的信息,包括:
- • 接口名:
com.example.mapper.UserMapper - • 方法名:
selectById - • 參數(shù):
1L
- • 接口名:
- 3. SQL 尋址: 這是最關(guān)鍵的一步。代理對(duì)象會(huì)根據(jù)**“接口名 + 方法名”**這個(gè)唯一的坐標(biāo),去 MyBatis 的全局配置中尋找與之對(duì)應(yīng)的 SQL 語(yǔ)句。
- 4. SQL 執(zhí)行: 找到 SQL 后,代理對(duì)象會(huì)利用底層的
SqlSession,將參數(shù)1L傳遞進(jìn)去,通過 JDBC 執(zhí)行這條 SQL。 - 5. 結(jié)果映射: 將數(shù)據(jù)庫(kù)返回的結(jié)果,根據(jù)配置映射成 Java 對(duì)象并返回。
所以,整個(gè)問題的關(guān)鍵就變成了:MyBatis 是如何完成第3步——**“SQL 尋址”**的?
2. “尋址”的藝術(shù):兩種主要的映射方式
MyBatis 提供了兩種方式,來告訴代理對(duì)象“接口方法”和“SQL語(yǔ)句”之間的對(duì)應(yīng)關(guān)系。
方式一:XML 映射文件 (最強(qiáng)大、最常用)
這是 MyBatis 最傳統(tǒng)也是功能最豐富的映射方式。它遵循兩條黃金法則:
法則1:namespace 必須是 Mapper 接口的全限定名。
這是連接 XML 文件和 Java 接口的“紅線”。
法則2:select/insert/update/delete 等標(biāo)簽的 id 必須與接口中的方法名完全一致。
這是定位到具體 SQL 語(yǔ)句的“門牌號(hào)”。
示例:
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() 時(shí),代理對(duì)象會(huì)精確地找到 namespace 為 com.example.mapper.UserMapper 的 XML 文件中,id 為 selectById 的 <select> 標(biāo)簽,并執(zhí)行其中的 SQL。
方式二:注解 (簡(jiǎn)單 SQL 的便捷之選)
對(duì)于簡(jiǎn)單的 SQL 語(yǔ)句,我們可以完全拋棄 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ù)雜,特別是需要?jiǎng)討B(tài) SQL(if, foreach等)時(shí),XML 的表現(xiàn)力遠(yuǎn)勝于注解。
3. 讓 MyBatis 找到你的 Mapper:掃描與注冊(cè)
我們已經(jīng)定義好了映射關(guān)系,但還需要告訴 MyBatis 去哪里“發(fā)現(xiàn)”這些接口和 XML 文件。
在現(xiàn)代 Spring Boot 項(xiàng)目中,這通常通過 mybatis-spring-boot-starter 自動(dòng)完成,我們只需提供少量配置。
1. @MapperScan 注解 (推薦)
在你的 Spring Boot 主啟動(dòng)類上,添加 @MapperScan 注解,并指定 Mapper 接口所在的包路徑。
@SpringBootApplication
@MapperScan("com.example.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}這個(gè)注解會(huì)告訴 MyBatis:“請(qǐng)掃描 com.example.mapper 包下的所有接口,并將它們注冊(cè)為 Mapper Bean。”
2. @Mapper 注解
你也可以不在啟動(dòng)類上使用 @MapperScan,而是在每個(gè) Mapper 接口上單獨(dú)添加 @Mapper 注解。
@Mapper
public interface UserMapper { ... }這樣,只要 UserMapper 接口在 Spring Boot 的組件掃描路徑下,它就會(huì)被自動(dòng)發(fā)現(xiàn)。但當(dāng) Mapper 接口很多時(shí),@MapperScan 顯然更方便。
3. XML 文件的位置
默認(rèn)情況下,MyBatis Spring Boot Starter 會(huì)在 classpath 中,尋找與 Mapper 接口同包同名的 XML 文件。
標(biāo)準(zhǔn)的 Maven 項(xiàng)目結(jié)構(gòu):
src/
main/
java/
com/
example/
mapper/
UserMapper.java
resources/
com/
example/
mapper/
UserMapper.xml <-- XML 放在 resources 目錄下,但包結(jié)構(gòu)與 Java 接口保持一致如果你想把 XML 文件集中存放在另一個(gè)地方,可以在 application.yml 中通過 mybatis.mapper-locations 屬性來指定:
mybatis: mapper-locations: classpath:mappers/*.xml
4. 常見問題與陷阱
BindingException: Type ... is not known to the MapperRegistry.
含義: “未在 Mapper 注冊(cè)表中找到該類型”。
原因: MyBatis 根本沒發(fā)現(xiàn)你的 Mapper 接口。請(qǐng)檢查 @MapperScan 的包路徑是否正確,或者接口上是否漏了 @Mapper 注解。
BindingException: Invalid bound statement (not found): ...
含義: “無效的綁定語(yǔ)句(未找到)”。
原因: 接口找到了,但接口里的方法沒找到對(duì)應(yīng)的 SQL。請(qǐng)檢查:
1. XML 的 namespace 是否是接口的全限定名,一個(gè)字母都不能錯(cuò)。
2. XML 標(biāo)簽的 id 是否與方法名完全一致。
3. XML 文件是否被構(gòu)建工具(Maven/Gradle)正確地打包進(jìn)了最終的 jar 文件中。
XML 文件未被打包:
如果你的 XML 文件放在 src/main/java 目錄下,Maven 默認(rèn)不會(huì)打包 .xml 文件。你需要在 pom.xml 中添加如下配置:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>總結(jié)
MyBatis 的接口映射機(jī)制,看似“神奇”,實(shí)則建立在一套清晰、嚴(yán)謹(jǐn)?shù)囊?guī)則之上:
- 1. 核心是 JDK 動(dòng)態(tài)代理,它在運(yùn)行時(shí)為你的接口創(chuàng)建了一個(gè)實(shí)現(xiàn)類。
- 2. 映射的“鑰匙”是“全限定接口名 + 方法名”。
- 3. 映射關(guān)系可以通過 XML 或注解來定義。
- 4.
@MapperScan是告知 MyBatis 從哪里開始尋找這些“鑰匙”的入口。
到此這篇關(guān)于MyBatis Mapper 接口是如何找到并執(zhí)行 SQL 的的文章就介紹到這了,更多相關(guān)MyBatis Mapper 接口執(zhí)行 SQL內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- mybatis中點(diǎn)擊mapper接口快速定位到對(duì)應(yīng)xml中sql方式
- 解決MyBatis Mapper的XML文件SQL語(yǔ)句無法自動(dòng)提示問題(親測(cè)有效)
- MyBatis的通俗理解:SqlSession.getMapper()源碼解讀
- mybatis?mapper.xml中如何根據(jù)數(shù)據(jù)庫(kù)類型選擇對(duì)應(yīng)SQL語(yǔ)句
- Mybatis?sqlMapConfig.xml中的mappers標(biāo)簽使用
- SpringBoot+Mybatis實(shí)現(xiàn)Mapper接口與Sql綁定幾種姿勢(shì)
- MyBatis直接執(zhí)行SQL的工具SqlMapper
- Mybatis中SqlMapper配置的擴(kuò)展與應(yīng)用詳細(xì)介紹(1)
相關(guān)文章
mybatis中foreach報(bào)錯(cuò):_frch_item_0 not found的解決方法
這篇文章主要給大家介紹了mybatis中foreach報(bào)錯(cuò):_frch_item_0 not found的解決方法,文章通過示例代碼介紹了詳細(xì)的解決方法,對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-06-06
mybatis-plus實(shí)現(xiàn)邏輯刪除的示例代碼
在大多數(shù)公司里,都會(huì)采用邏輯刪除的方式,本文主要介紹了mybatis-plus實(shí)現(xiàn)邏輯刪除的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05
java捕獲AOP級(jí)別的異常并將其傳遞到Controller層
如何在一個(gè)現(xiàn)代的Java應(yīng)用中,捕獲AOP(面向切面編程)級(jí)別的異常,并將這些異常傳遞到Controller層進(jìn)行合適的處理,異常處理在構(gòu)建可靠的應(yīng)用程序中起著關(guān)鍵作用,而AOP則可以幫助我們更好地管理和組織代碼,我們將深入研究如何結(jié)合AOP和異常處理來構(gòu)建健壯的應(yīng)用2023-09-09
一文詳解java如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
從?Java?8?開始,便引入了一種稱為“流式?API”的編程風(fēng)格,當(dāng)然也被稱為“鏈?zhǔn)皆O(shè)置”或“鏈?zhǔn)秸{(diào)用”,本文主要來和大家討論一下如何實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,感興趣的可以了解下2023-12-12
Servlet+JavaBean+JSP打造Java Web注冊(cè)與登錄功能
比作MVC的話,控制器部分采用Servlet來實(shí)現(xiàn),模型部分采用JavaBean來實(shí)現(xiàn),而大部分的視圖采用Jsp頁(yè)面來實(shí)現(xiàn),接下來我們就來詳細(xì)看看如何用Servlet+JavaBean+JSP打造Java Web注冊(cè)與登錄功能2016-05-05
java使用DOM4J對(duì)XML文件進(jìn)行增刪改查操作
這篇文章主要為大家詳細(xì)介紹了java使用DOM4J對(duì)XML文件進(jìn)行增刪改查操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
使用IDEA工具配置和運(yùn)行vue項(xiàng)目及遇到的坑
這篇文章主要介紹了使用IDEA工具配置和運(yùn)行vue項(xiàng)目及遇到的坑,需要的朋友可以參考下2018-09-09
java 中序列化與readResolve()方法的實(shí)例詳解
這篇文章主要介紹了java 中序列化與readResolve()方法的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例幫助大家理解這部分知識(shí),需要的朋友可以參考下2017-08-08

