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

