MyBatis 結(jié)果映射的幾種實(shí)現(xiàn)方式
一、 什么是結(jié)果映射? ??
在我們的 Java 應(yīng)用程序中,數(shù)據(jù)通常以對(duì)象(JavaBeans/POJOs)的形式存在。然而,關(guān)系型數(shù)據(jù)庫(kù)是以行和列的形式存儲(chǔ)數(shù)據(jù)。當(dāng)我們從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)時(shí),得到的是一個(gè)包含多行多列的結(jié)果集(ResultSet)。
結(jié)果映射(Result Mapping) 就是 MyBatis 中定義如何將數(shù)據(jù)庫(kù)查詢結(jié)果集中的列(Columns)映射到 Java 對(duì)象的屬性(Properties)上的規(guī)則和過(guò)程。它是解決數(shù)據(jù)庫(kù)中心的數(shù)據(jù)表示(表結(jié)構(gòu))與應(yīng)用程序中心的對(duì)象模型(類結(jié)構(gòu))之間“阻抗失配”問(wèn)題的核心機(jī)制。
簡(jiǎn)單來(lái)說(shuō): 告訴 MyBatis,數(shù)據(jù)庫(kù)查出來(lái)的 user_id 這一列的值,應(yīng)該賦給 User 這個(gè) Java 類的 userId 這個(gè)屬性。
二、 為何需要結(jié)果映射? ??
- 命名差異:數(shù)據(jù)庫(kù)列名常用下劃線命名法(
snake_case,如order_id),而 Java 屬性常用駝峰命名法(camelCase,如orderId)。需要轉(zhuǎn)換。 - 類型轉(zhuǎn)換 ??:數(shù)據(jù)庫(kù)類型(如
VARCHAR,TIMESTAMP,NUMBER)需要轉(zhuǎn)換為相應(yīng)的 Java 類型(如String,java.util.Date,Integer,BigDecimal)。 - 復(fù)雜關(guān)系:實(shí)際應(yīng)用中,數(shù)據(jù)往往不是扁平的。一個(gè)對(duì)象可能包含另一個(gè)對(duì)象(一對(duì)一/多對(duì)一),或者包含一個(gè)對(duì)象集合(一對(duì)多)。需要機(jī)制來(lái)處理這些關(guān)聯(lián)。
- 顯式控制 ??:有時(shí)自動(dòng)映射無(wú)法滿足需求,需要更精細(xì)地控制映射過(guò)程,例如列名和屬性名差異很大,或需要使用自定義的類型處理器(TypeHandler)。
三、 如何映射?(基礎(chǔ))?
MyBatis 提供了多種方式進(jìn)行結(jié)果映射:
1、 別名映射 ??
這是最直接的方式,在 SQL 語(yǔ)句中使用 AS 關(guān)鍵字為查詢的列指定別名,讓別名與 Java 對(duì)象的屬性名完全一致。
Java Bean (User.java)
public class User {
private Integer userId;
private String userName;
private String userEmail;
// getters and setters...
}
Mapper XML (UserMapper.xml)
<select id="findUserById" resultType="com.yourcompany.domain.User">
SELECT
user_id AS userId, -- 使用 AS 將列名映射到屬性名
user_name AS userName,
email AS userEmail -- 列名和屬性名不一致時(shí)必須用 AS
FROM users
WHERE user_id = #{id}
</select>
- 優(yōu)點(diǎn):非常明確,SQL 本身就定義了映射關(guān)系。
- 缺點(diǎn):SQL 語(yǔ)句會(huì)變得冗長(zhǎng),尤其當(dāng)字段很多時(shí)。SQL 摻雜了部分映射邏輯。
2、 駝峰命名自動(dòng)映射??
這是 MyBatis 提供的一個(gè)便捷功能。開(kāi)啟后,MyBatis 會(huì)自動(dòng)嘗試將下劃線命名法的列(column_name)映射到駝峰命名法的屬性(columnName)。
Java Bean (User.java) - 同上
Mapper XML (UserMapper.xml)
<select id="findUserById" resultType="com.yourcompany.domain.User">
SELECT
user_id, -- 無(wú)需 AS
user_name,
email -- 如果屬性名是 email,也能自動(dòng)映射
FROM users
WHERE user_id = #{id}
</select>
(注意:如果 Java 屬性名不是標(biāo)準(zhǔn)的駝峰轉(zhuǎn)換,駝峰映射也無(wú)法自動(dòng)處理)
如何開(kāi)啟?
MyBatis 配置文件 (mybatis-config.xml):
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
Spring Boot 配置文件 (application.properties 或 application.yml):
# application.properties mybatis.configuration.map-underscore-to-camel-case=true
# application.yml
mybatis:
configuration:
map-underscore-to-camel-case: true
通過(guò) Java 配置 (如 Spring):
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
// ... 其他配置
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true); // 開(kāi)啟駝峰映射
factoryBean.setConfiguration(configuration);
// ... 其他配置
return factoryBean.getObject();
}
- 優(yōu)點(diǎn):保持 SQL 簡(jiǎn)潔 ?,配置一次全局生效,是目前推薦的主流簡(jiǎn)單映射方式。
- 缺點(diǎn):需要配置開(kāi)啟;對(duì)于非標(biāo)準(zhǔn)駝峰映射的場(chǎng)景無(wú)能為力。
3、 resultMap 顯式映射 ???
當(dāng)自動(dòng)映射(包括駝峰)無(wú)法滿足需求,或者需要處理復(fù)雜類型、關(guān)聯(lián)查詢時(shí),resultMap 是最強(qiáng)大、最靈活的武器。它允許你顯式地、精確地定義列與屬性之間的映射關(guān)系。
Java Bean (User.java) - 同上
Mapper XML (UserMapper.xml)
定義 <resultMap>:
<resultMap id="userResultMap" type="com.yourcompany.domain.User">
<!-- <id> 標(biāo)簽用于映射主鍵字段,有助于 MyBatis 性能優(yōu)化 -->
<id property="userId" column="user_id" />
<!-- <result> 標(biāo)簽用于映射普通字段 -->
<result property="userName" column="user_name"/>
<result property="userEmail" column="email"/>
<!-- 可以指定 javaType 和 jdbcType,通常 MyBatis 能自動(dòng)推斷 -->
<!-- <result property="status" column="user_status" javaType="java.lang.Integer" jdbcType="INTEGER"/> -->
</resultMap>
在 <select> 標(biāo)簽中使用 resultMap 屬性引用:
<select id="findUserById" resultMap="userResultMap">
SELECT user_id, user_name, email FROM users WHERE user_id = #{id}
</select>
<resultMap>屬性:id: 在當(dāng)前 Mapper XML 命名空間內(nèi)的唯一標(biāo)識(shí)符。type: 映射的目標(biāo) Java 類的完全限定名或 MyBatis 配置的別名。
子標(biāo)簽
<id>和<result>屬性:property: Java 對(duì)象的屬性名。column: SQL 查詢結(jié)果集中的列名(或別名)。javaType: Java 屬性的完整類名或別名(通??墒÷裕?。jdbcType: 數(shù)據(jù)庫(kù)列的 JDBC 類型(枚舉org.apache.ibatis.type.JdbcType)。typeHandler: 指定自定義的類型處理器。
優(yōu)點(diǎn):控制力最強(qiáng) ??!能處理各種復(fù)雜映射場(chǎng)景,是實(shí)現(xiàn)高級(jí)映射的基礎(chǔ)。代碼更清晰,SQL 保持純凈。
缺點(diǎn):配置相對(duì)繁瑣,增加了 XML 的代碼量。
四、 高級(jí)結(jié)果映射 (處理關(guān)聯(lián)關(guān)系) ??
當(dāng)查詢結(jié)果需要映射到包含其他對(duì)象或?qū)ο蠹系膹?fù)雜 Java 對(duì)象時(shí),就需要使用 resultMap 的高級(jí)特性:<association> 和 <collection>。
場(chǎng)景設(shè)定:
- 實(shí)體類:
Department.java: 包含id,name屬性。Employee.java: 包含id,name,email,department(Department 對(duì)象),departmentId(Integer)。
- 數(shù)據(jù)庫(kù)表:
departments(id PK, dept_name)employees(id PK, emp_name, emp_email, dept_id FK references departments.id)
1、<association>(處理 “有一個(gè)” 關(guān)系 - To-One) ????
用于映射一個(gè)對(duì)象中包含的另一個(gè)對(duì)象,通常對(duì)應(yīng)數(shù)據(jù)庫(kù)中的 一對(duì)一 或 多對(duì)一 關(guān)系。
兩種主要實(shí)現(xiàn)方式:
1. 嵌套 Select 查詢 (Nested Select)
- 思路: 先查詢主對(duì)象(Employee),然后根據(jù)外鍵(
dept_id),再執(zhí)行一個(gè)單獨(dú)的 SQL 查詢獲取關(guān)聯(lián)對(duì)象(Department)。 - Mapper XML:
<select id="findDepartmentById" resultType="com.yourcompany.domain.Department">...</select> <resultMap id="employeeWithDeptSelectMap" type="com.yourcompany.domain.Employee"> <id property="id" column="id"/> <result property="name" column="emp_name"/> <!-- association: 映射 department 屬性 --> <association property="department" javaType="com.yourcompany.domain.Department" column="dept_id" select="findDepartmentById" /> <!-- column: 傳遞給 select 查詢的參數(shù)列 --> <!-- select: 指定用于查詢關(guān)聯(lián)對(duì)象的 select 語(yǔ)句 ID --> <!-- fetchType="lazy": 可配置懶加載 --> </resultMap> <select id="findEmployeeByIdWithDeptSelect" resultMap="employeeWithDeptSelectMap">...</select> - 優(yōu)點(diǎn): 簡(jiǎn)單直觀,SQL 語(yǔ)句分離,支持懶加載。
- 缺點(diǎn): N+1 查詢問(wèn)題!性能可能很差 ??,通常不推薦使用 ?。
2. 嵌套結(jié)果映射 (Nested Result Map) ?
- 思路: 使用 SQL 的
JOIN操作一次性查詢出所有需要的數(shù)據(jù),然后在resultMap中定義嵌套結(jié)構(gòu)來(lái)映射連接后的結(jié)果。 - Mapper XML:
<resultMap id="employeeWithDeptNestedMap" type="com.yourcompany.domain.Employee"> <id property="id" column="emp_id"/> <result property="name" column="emp_name"/> <result property="departmentId" column="dept_id"/> <!-- association: 映射 department 屬性 --> <association property="department" javaType="com.yourcompany.domain.Department"> <!-- 嵌套定義 Department 的映射規(guī)則 --> <id property="id" column="dept_id"/> <result property="name" column="dept_name"/> </association> </resultMap> <select id="findEmployeeByIdWithDeptNested" resultMap="employeeWithDeptNestedMap"> SELECT e.id AS emp_id, e.emp_name, e.emp_email, e.dept_id, d.id AS dept_id, d.dept_name -- 查詢關(guān)聯(lián)表的列,注意別名 FROM employees e LEFT JOIN departments d ON e.dept_id = d.id WHERE e.id = #{empId} </select> - 優(yōu)點(diǎn): 性能好 ??,只需執(zhí)行一次 SQL 查詢。結(jié)構(gòu)清晰。這是處理關(guān)聯(lián)關(guān)系推薦的方式 ?。
- 缺點(diǎn): SQL 語(yǔ)句可能因 JOIN 變得復(fù)雜;不支持懶加載(但通常性能好就不需要了)。
2、<collection>(處理 “有很多” 關(guān)系 - To-Many) ????
用于映射一個(gè)對(duì)象中包含的對(duì)象集合,通常對(duì)應(yīng)數(shù)據(jù)庫(kù)中的 一對(duì)多 關(guān)系。
同樣有兩種實(shí)現(xiàn)方式:
1. 嵌套 Select 查詢 (Nested Select)
- 思路: 先查詢主對(duì)象(Department),然后根據(jù)主鍵(
id),再執(zhí)行一個(gè)單獨(dú)的 SQL 查詢獲取關(guān)聯(lián)的對(duì)象集合(List)。 - Java Bean (
Department.java): (需要有List<Employee> employees;屬性) - Mapper XML:
<select id="findEmployeesByDeptId" resultType="com.yourcompany.domain.Employee">...</select> <resultMap id="deptWithEmployeesSelectMap" type="com.yourcompany.domain.Department"> <id property="id" column="id"/> <result property="name" column="dept_name"/> <!-- collection: 映射 employees 集合屬性 --> <collection property="employees" ofType="com.yourcompany.domain.Employee" column="id" select="findEmployeesByDeptId" /> <!-- column: 傳遞給 select 查詢的參數(shù)列 --> <!-- select: 指定用于查詢關(guān)聯(lián)集合的 select 語(yǔ)句 ID --> <!-- ofType: 指定集合中元素的類型 --> <!-- fetchType="lazy": 支持懶加載 --> </resultMap> <select id="findDeptByIdWithEmployeesSelect" resultMap="deptWithEmployeesSelectMap">...</select> - 優(yōu)點(diǎn): 簡(jiǎn)單直觀,SQL 分離,支持懶加載。
- 缺點(diǎn): N+1 查詢問(wèn)題!性能問(wèn)題嚴(yán)重 ??,通常不推薦使用 ?。
2. 嵌套結(jié)果映射 (Nested Result Map) ?
- 思路: 使用 SQL 的
LEFT JOIN一次性查詢出部門(mén)及其所有員工的信息。MyBatis 的resultMap機(jī)制能智能地將重復(fù)的主對(duì)象信息合并,并將關(guān)聯(lián)的從對(duì)象信息收集到集合中。 - Mapper XML:
<resultMap id="employeeBaseMap" type="com.yourcompany.domain.Employee">...</resultMap> <resultMap id="deptWithEmployeesNestedMap" type="com.yourcompany.domain.Department"> <id property="id" column="dept_id"/> <!-- 部門(mén) ID 是主鍵 --> <result property="name" column="dept_name"/> <!-- collection: 映射 employees 集合屬性 --> <collection property="employees" ofType="com.yourcompany.domain.Employee" resultMap="employeeBaseMap" /> <!-- ofType: 指定集合元素類型 --> <!-- resultMap: 引用 Employee resultMap 來(lái)映射集合中的每個(gè)對(duì)象 --> <!-- 或者可以直接在 collection 內(nèi)部定義映射規(guī)則 --> </resultMap> <select id="findDeptByIdWithEmployeesNested" resultMap="deptWithEmployeesNestedMap"> SELECT d.id AS dept_id, d.dept_name, e.id AS emp_id, e.emp_name, e.emp_email FROM departments d LEFT JOIN employees e ON d.id = e.dept_id WHERE d.id = #{deptId} </select> - 優(yōu)點(diǎn): 性能好 ??,只需一次 SQL 查詢。MyBatis 自動(dòng)處理結(jié)果聚合。映射邏輯集中。這是處理一對(duì)多關(guān)系推薦的方式 ?。
- 缺點(diǎn): SQL 語(yǔ)句可能較復(fù)雜;返回的數(shù)據(jù)量可能較大(主表信息會(huì)重復(fù))。
五、 選擇哪種映射方式? ??
- 簡(jiǎn)單場(chǎng)景(列名與屬性名一致或符合駝峰規(guī)則):優(yōu)先使用駝峰自動(dòng)映射 ??。
- 列名與屬性名不一致/不規(guī)則:少數(shù)幾個(gè)字段用 SQL 別名
AS??;較多或想保持 SQL 純凈,使用resultMap???。 - 需要類型轉(zhuǎn)換/自定義 TypeHandler:必須使用
resultMap??。 - 處理一對(duì)一/多對(duì)一/一對(duì)多關(guān)聯(lián)關(guān)系:必須使用
resultMap的<association>或<collection>??。強(qiáng)烈推薦使用 嵌套結(jié)果映射 (Nested Result Map) 方式(基于 JOIN)?,避免 N+1 問(wèn)題。只有在明確需要懶加載且能接受其潛在性能影響時(shí),才考慮嵌套 Select。
六、 總結(jié) ??
MyBatis 的結(jié)果映射機(jī)制是其強(qiáng)大功能的核心之一。從簡(jiǎn)單的別名、駝峰自動(dòng)映射到強(qiáng)大的 resultMap(包括處理復(fù)雜關(guān)聯(lián)的 <association> 和 <collection>),它提供了靈活多樣的手段來(lái)連接數(shù)據(jù)庫(kù)表結(jié)構(gòu)和 Java 對(duì)象模型。理解并熟練運(yùn)用這些映射方式,特別是掌握 resultMap 的高級(jí)用法并優(yōu)先選擇嵌套結(jié)果映射來(lái)處理關(guān)聯(lián),對(duì)于編寫(xiě)高效、可維護(hù)的 MyBatis 應(yīng)用至關(guān)重要。
到此這篇關(guān)于MyBatis 結(jié)果映射的幾種實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)MyBatis 結(jié)果映射內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis延遲加載、關(guān)聯(lián)查詢與結(jié)果映射的實(shí)現(xiàn)原理解析
- 深度分析MybatisPlus查詢結(jié)果映射失敗@TableField失效解決辦法
- MyBatis 結(jié)果映射的兩種方式
- MyBatis結(jié)果映射(ResultMap)的使用
- MyBatis動(dòng)態(tài)SQL、模糊查詢與結(jié)果映射操作過(guò)程
- Java中MyBatis的結(jié)果映射詳解
- MyBatis中的SQL映射文件配置結(jié)果映射的操作指南
- 關(guān)于MyBatis結(jié)果映射的實(shí)例總結(jié)
- 基于mybatis查詢結(jié)果映射不到對(duì)象的處理
相關(guān)文章
Java世界時(shí)區(qū)自動(dòng)計(jì)算及時(shí)間生成方法詳解
這篇文章主要為大家詳細(xì)介紹了Java中世界時(shí)區(qū)自動(dòng)計(jì)算及時(shí)間生成的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-12-12
以Json形式的數(shù)據(jù)格式實(shí)現(xiàn)JMeter參數(shù)化
本文以小項(xiàng)目學(xué)院管理系統(tǒng)為例,給大家分享以Json形式的數(shù)據(jù)格式實(shí)現(xiàn)JMeter參數(shù)化的相關(guān)知識(shí),包括添加元件操作步驟及使用用戶參數(shù)組件實(shí)現(xiàn)參數(shù)化的方法,感興趣的朋友跟隨小編一起看看吧2021-05-05
Javaweb監(jiān)聽(tīng)器實(shí)例之統(tǒng)計(jì)在線人數(shù)
這篇文章主要為大家詳細(xì)介紹了Javaweb監(jiān)聽(tīng)器實(shí)例之統(tǒng)計(jì)在線人數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
springBoot啟動(dòng)輸出三行日志控制臺(tái)自動(dòng)停止操作
這篇文章主要介紹了springBoot啟動(dòng)輸出三行日志控制臺(tái)自動(dòng)停止操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Spring中為bean指定InitMethod和DestroyMethod的執(zhí)行方法
在Spring中,那些組成應(yīng)用程序的主體及由Spring IoC容器所管理的對(duì)象,被稱之為bean,接下來(lái)通過(guò)本文給大家介紹Spring中為bean指定InitMethod和DestroyMethod的執(zhí)行方法,感興趣的朋友一起看看吧2021-11-11
Java日期毫秒值和常見(jiàn)日期時(shí)間格式相互轉(zhuǎn)換方法
這篇文章主要給大家介紹了關(guān)于Java日期毫秒值和常見(jiàn)日期時(shí)間格式相互轉(zhuǎn)換的相關(guān)資料,在Java的日常開(kāi)發(fā)中,會(huì)隨時(shí)遇到需要對(duì)時(shí)間處理的情況,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考下2023-07-07

