實體類之間存在關聯關係(一對一、一對多)時,MyBatis 通過 <resultMap> 的 <association>(一對一)和 <collection>(一對多)標籤實現關聯對象自動映射。
public class Website {
private int id;
private String name;
private int visitorId;
private int status;
private Date createTime;
private Visitor visitor; // 關聯對象
// getter/setter/toString...
}
<mapper namespace="david.mybatis.demo.IWebsiteOperation">
<!-- 關聯查詢:Website + Visitor -->
<select id="query" parameterType="int" resultMap="websiteRs">
select
w.id as siteId, w.name as siteName,
v.id as visitorId, v.name as visitorName,
w.status as siteStatus, w.createtime as siteCreateTime
from Website w
inner join Visitor v on w.visitorid = v.id
where w.status > 0 and w.id = #{id}
</select>
<resultMap type="Website" id="websiteRs">
<id column="siteId" property="id" />
<result column="siteName" property="name" />
<result column="siteStatus" property="status" />
<result column="siteCreateTime" property="createTime" />
<!-- 關鍵:association 映射一對一關聯對象 -->
<association property="visitor" javaType="Visitor" resultMap="visitorRs" />
</resultMap>
<resultMap type="Visitor" id="visitorRs">
<id column="visitorId" property="id" />
<result column="visitorName" property="name" />
</resultMap>
</mapper>
id/name 字段,必須起別名(如 siteId、visitorId)避免映射錯亂property 對應實體屬性名(visitor),javaType 指定關聯類型,resultMap 引用關聯對象的映射定義定義分頁參數對象:
public class PagenateArgs {
private int pageIndex; // 頁碼(從 0 開始)
private int pageSize; // 每頁條數
private int pageStart; // 起始行 = pageIndex * pageSize
private String orderFieldStr; // 排序字段
private String orderDirectionStr; // ASC/DESC
public PagenateArgs(int pageIndex, int pageSize, String orderField, String orderDirection) {
this.pageIndex = pageIndex;
this.pageSize = pageSize;
this.orderFieldStr = orderField;
this.orderDirectionStr = orderDirection;
this.pageStart = pageIndex * pageSize;
}
// getter...
}
Mapper 接口與 XML:
// 接口
List<Visitor> getListByPagenate(PagenateArgs args);
<select id="getListByPagenate" parameterType="PagenateArgs" resultType="Visitor">
select * from (
<include refid="getListSql" />
<include refid="orderBySql"/>
) t
<if test="pageStart > -1 and pageSize > -1">
limit #{pageStart}, #{pageSize}
</if>
</select>
<sql id="orderBySql"> order by ${orderFieldStr} ${orderDirectionStr} </sql>
調用示例:
PagenateArgs args = new PagenateArgs(1, 5, "id", "DESC"); // 第2頁,每頁5條,ID倒序
List<Visitor> pageData = vOperation.getListByPagenate(args);
MySQL 8.0+ 支持窗口函數,舊版本可用變量模擬 ROW_NUMBER():
<resultMap type="VisitorWithRn" id="visitorWithRnRs">
<id column="Id" property="id" />
<result column="Name" property="name" />
<result column="Email" property="email" />
<result column="Status" property="status" />
<result column="CreateTime" property="createTime" />
<result column="Rownum" property="rownum" /> <!-- 行號映射 -->
</resultMap>
<select id="getListByPagenateWithRn" resultMap="visitorWithRnRs">
select t.Rownum, t.Id, t.Name, t.Email, t.Status, t.CreateTime from (
<include refid="getListSqlContainsRn" />
<include refid="orderBySql"/>
) t
<if test="pageStart > -1 and pageSize > -1">
limit #{pageStart}, #{pageSize}
</if>
</select>
<sql id="getListSqlContainsRn">
select @rownum:=@rownum+1 as Rownum, result.id, result.name, result.email, result.status, result.createTime
from (select @rownum:=0, Visitor.* from Visitor where status > 0) result
</sql>
引入依賴:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.3</version>
</dependency>
配置攔截器(mybatis-config.xml):
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<property name="reasonable" value="true"/>
<property name="supportMethodsArguments" value="true"/>
<property name="params" value="count=countSql"/>
</plugin>
</plugins>
業務代碼極簡:
PageHelper.startPage(pageNum, pageSize, "id DESC");
List<Visitor> list = visitorMapper.getList(); // 自動攔截添加 LIMIT
PageInfo<Visitor> pageInfo = new PageInfo<>(list); // 獲取總數、總頁數等
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 手寫 LIMIT | 零依賴、完全可控、學習價值高 | 需手動計數總條數、跨數據庫兼容性差 | 簡單項目、教學演示、特殊分頁邏輯 |
| PageHelper | 非侵入式、支持多種數據庫、功能豐富 | 引入第三方依賴、原理較複雜 | 企業級項目、標準分頁需求、團隊協作 |
| MyBatis-Plus | 內置分頁、CRUD 自動生成、條件構造器 | 框架侵入性強、學習成本較高 | 新項目、追求開發效率、標準化開發 |
PagenateArgs 統一管理分頁、排序參數WHERE id > lastId LIMIT size)${orderField} 注入風險MyBatis 通過強大的 resultMap 機制優雅解決多表關聯映射問題,<association> 處理一對一,<collection> 處理一對多。分頁查詢方面,手寫 LIMIT 適合學習與簡單場景,生產環境強烈建議接入 PageHelper 或 MyBatis-Plus 等成熟分頁插件,實現零侵入、跨數據庫的標準化分頁能力。掌握這兩大核心技能,是 MyBatis 進階開發者的必修課。