当前位置: 首页 > news >正文

mapper层学习

Mapper层学习文档

概述

本文档详细介绍MyBatis Mapper层的核心概念、实现方式和最佳实践,以项目中的DictDataMapper.xml为例,帮助开发人员深入理解Mapper层的设计和使用。


1. Mapper层架构概述

1.1 Mapper层在项目中的位置

Controller层 (接收请求)↓
Service层 (业务逻辑)↓
Mapper层 (数据访问)↓
数据库 (数据存储)

1.2 Mapper层的作用

  • 数据访问抽象:提供统一的数据访问接口
  • SQL映射:将Java方法映射到SQL语句
  • 结果映射:将数据库查询结果映射为Java对象
  • 动态SQL:根据条件动态生成SQL语句

2. Mapper接口定义

2.1 接口规范

package com.hn.bitao.defined.report.module.mapper;import com.hn.bitao.defined.report.module.pojo.DictData;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;/*** 字典数据Mapper接口* * @author 开发团队* @since 1.0.0*/
@Mapper
public interface DictDataMapper {/*** 新增字典数据* @param dictData 字典数据* @return 影响行数*/int insert(DictData dictData);/*** 选择性新增字典数据(只插入非空字段)* @param dictData 字典数据* @return 影响行数*/int insertSelective(DictData dictData);/*** 根据主键删除字典数据* @param dictCode 字典编码* @return 影响行数*/int deleteByPrimaryKey(Long dictCode);/*** 根据主键更新字典数据* @param dictData 字典数据* @return 影响行数*/int updateByPrimaryKey(DictData dictData);/*** 选择性更新字典数据(只更新非空字段)* @param dictData 字典数据* @return 影响行数*/int updateByPrimaryKeySelective(DictData dictData);/*** 根据主键查询字典数据* @param dictCode 字典编码* @return 字典数据*/DictData selectByPrimaryKey(Long dictCode);/*** 查询字典数据列表(支持动态条件)* @param dictData 查询条件* @return 字典数据列表*/List<DictData> selectDictDataList(DictData dictData);/*** 根据字典类型查询字典数据列表* @param dictType 字典类型* @return 字典数据列表*/List<DictData> selectByDictType(String dictType);/*** 根据字典类型统计数量* @param dictType 字典类型* @return 数据数量*/int countByDictType(String dictType);
}

2.2 接口命名规范

操作类型命名规范示例
新增insert / insertSelectiveinsert(DictData dictData)
删除deleteBy + 条件deleteByPrimaryKey(Long id)
修改updateBy + 条件updateByPrimaryKey(DictData dictData)
查询单个selectBy + 条件selectByPrimaryKey(Long id)
查询列表select + 实体名 + ListselectDictDataList(DictData dictData)
统计数量countBy + 条件countByDictType(String dictType)

3. XML映射文件结构

3.1 文件头部配置

<?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.hn.bitao.defined.report.module.mapper.DictDataMapper">

关键要素:

  • namespace:必须与Mapper接口的完整类名一致
  • DTD声明:定义MyBatis映射文件的语法规则

3.2 结果映射(ResultMap)

3.2.1 基础结果映射概述

基础结果映射是MyBatis中用于定义数据库查询结果与Java对象属性之间映射关系的核心配置。它解决了数据库字段名与Java对象属性名不一致的问题,是MyBatis ORM功能的基础。

主要作用:

  • 字段映射:建立数据库字段与Java对象属性的对应关系
  • 类型转换:自动处理数据库类型与Java类型之间的转换
  • 性能优化:通过主键映射提高查询性能
  • 代码复用:一次定义,多处使用,提高开发效率
3.2.2 基础结果映射语法结构
<!-- 基础结果映射 -->
<resultMap id="BaseResultMap" type="com.hn.bitao.defined.report.module.pojo.DictData"><id property="dictCode" column="dict_code" jdbcType="BIGINT" /><result property="dictSort" column="dict_sort" jdbcType="INTEGER" /><result property="dictLabel" column="dict_label" jdbcType="VARCHAR" /><result property="dictValue" column="dict_value" jdbcType="VARCHAR" /><result property="dictType" column="dict_type" jdbcType="VARCHAR" /><result property="cssClass" column="css_class" jdbcType="VARCHAR" /><result property="listClass" column="list_class" jdbcType="VARCHAR" /><result property="isDefault" column="is_default" jdbcType="CHAR" /><result property="status" column="status" jdbcType="CHAR" /><result property="createBy" column="create_by" jdbcType="VARCHAR" /><result property="createTime" column="create_time" jdbcType="TIMESTAMP" /><result property="updateBy" column="update_by" jdbcType="VARCHAR" /><result property="updateTime" column="update_time" jdbcType="TIMESTAMP" /><result property="remark" column="remark" jdbcType="VARCHAR" />
</resultMap>
3.2.3 ResultMap核心要素详解

1. resultMap标签属性:

  • id:ResultMap的唯一标识符,用于在SQL语句中引用
  • type:映射的目标Java类型(完整类名或类型别名)
  • autoMapping:是否启用自动映射(可选,默认true)
  • extends:继承其他ResultMap(可选)

2. 子元素类型:

元素作用使用场景
<id>主键映射标识主键字段,用于性能优化和缓存
<result>普通字段映射映射非主键字段
<association>一对一关联映射复杂对象属性
<collection>一对多关联映射集合属性
<discriminator>鉴别器根据某个字段值选择不同的映射

3. 字段映射属性:

  • property:Java对象的属性名(必填)
  • column:数据库字段名(必填)
  • jdbcType:JDBC数据类型(可选,建议填写)
  • javaType:Java数据类型(可选,通常可自动推断)
  • typeHandler:自定义类型处理器(可选)
3.2.4 字段映射规则

命名转换规则:

<!-- 数据库下划线命名 -> Java驼峰命名 -->
<result property="dictCode" column="dict_code" />        <!-- dict_code -> dictCode -->
<result property="createTime" column="create_time" />    <!-- create_time -> createTime -->
<result property="isDefault" column="is_default" />      <!-- is_default -> isDefault -->

类型映射对应关系:

<!-- 常用JDBC类型映射 -->
<result property="id" column="id" jdbcType="BIGINT" />           <!-- Long -->
<result property="name" column="name" jdbcType="VARCHAR" />       <!-- String -->
<result property="age" column="age" jdbcType="INTEGER" />         <!-- Integer -->
<result property="price" column="price" jdbcType="DECIMAL" />     <!-- BigDecimal -->
<result property="isActive" column="is_active" jdbcType="CHAR" />  <!-- String/Boolean -->
<result property="createTime" column="create_time" jdbcType="TIMESTAMP" /> <!-- Date/LocalDateTime -->
3.2.5 高级结果映射示例

1. 继承结果映射:

<!-- 基础映射 -->
<resultMap id="BaseResultMap" type="DictData"><id property="dictCode" column="dict_code" /><result property="dictLabel" column="dict_label" /><result property="dictValue" column="dict_value" />
</resultMap><!-- 扩展映射(继承基础映射) -->
<resultMap id="ExtendedResultMap" type="DictData" extends="BaseResultMap"><result property="createTime" column="create_time" /><result property="updateTime" column="update_time" /><result property="remark" column="remark" />
</resultMap>

2. 自定义类型处理器:

<resultMap id="BaseResultMap" type="DictData"><result property="status" column="status" typeHandler="com.example.handler.StatusTypeHandler" /><result property="createTime" column="create_time" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler" />
</resultMap>

3. 复杂对象映射:

<resultMap id="DictDataWithTypeInfo" type="DictData"><id property="dictCode" column="dict_code" /><result property="dictLabel" column="dict_label" /><result property="dictValue" column="dict_value" /><!-- 一对一关联映射 --><association property="dictTypeInfo" javaType="DictType"><id property="dictId" column="type_id" /><result property="dictName" column="type_name" /><result property="dictType" column="type_code" /></association>
</resultMap>
3.2.6 ResultMap使用最佳实践,不同的映射resultMap

1. 命名规范:

<!-- 推荐的命名方式 -->
<resultMap id="BaseResultMap" type="DictData">          <!-- 基础映射 -->
<resultMap id="DetailResultMap" type="DictData">       <!-- 详细映射 -->
<resultMap id="SimpleResultMap" type="DictData">       <!-- 简化映射 -->
<resultMap id="WithAssociationResultMap" type="DictData"> <!-- 关联映射 -->

2. 性能优化:

<resultMap id="BaseResultMap" type="DictData"><!-- 主键映射必须使用id标签,不要使用result --><id property="dictCode" column="dict_code" jdbcType="BIGINT" /><!-- 明确指定jdbcType,避免类型推断开销 --><result property="dictLabel" column="dict_label" jdbcType="VARCHAR" /><!-- 对于可能为null的字段,建议指定jdbcType --><result property="remark" column="remark" jdbcType="VARCHAR" />
</resultMap>

3. 维护性考虑:

<!-- 按字段重要性和使用频率排序 -->
<resultMap id="BaseResultMap" type="DictData"><!-- 主键字段 --><id property="dictCode" column="dict_code" jdbcType="BIGINT" /><!-- 核心业务字段 --><result property="dictLabel" column="dict_label" jdbcType="VARCHAR" /><result property="dictValue" column="dict_value" jdbcType="VARCHAR" /><result property="dictType" column="dict_type" jdbcType="VARCHAR" /><!-- 状态和排序字段 --><result property="dictSort" column="dict_sort" jdbcType="INTEGER" /><result property="status" column="status" jdbcType="CHAR" /><!-- 扩展字段 --><result property="cssClass" column="css_class" jdbcType="VARCHAR" /><result property="listClass" column="list_class" jdbcType="VARCHAR" /><result property="isDefault" column="is_default" jdbcType="CHAR" /><!-- 审计字段 --><result property="createBy" column="create_by" jdbcType="VARCHAR" /><result property="createTime" column="create_time" jdbcType="TIMESTAMP" /><result property="updateBy" column="update_by" jdbcType="VARCHAR" /><result property="updateTime" column="update_time" jdbcType="TIMESTAMP" /><result property="remark" column="remark" jdbcType="VARCHAR" />
</resultMap>
3.2.7 常见问题与解决方案

问题1:字段映射不生效

<!-- 错误:property名称与Java属性不匹配 -->
<result property="dictcode" column="dict_code" />  <!-- 应该是dictCode --><!-- 正确:确保property名称与Java属性完全一致 -->
<result property="dictCode" column="dict_code" />

问题2:类型转换异常

<!-- 错误:没有指定正确的jdbcType -->
<result property="createTime" column="create_time" /><!-- 正确:明确指定jdbcType -->
<result property="createTime" column="create_time" jdbcType="TIMESTAMP" />

问题3:主键映射性能问题

<!-- 错误:主键使用result标签 -->
<result property="dictCode" column="dict_code" /><!-- 正确:主键必须使用id标签 -->
<id property="dictCode" column="dict_code" jdbcType="BIGINT" />

总结:
基础结果映射是MyBatis中最重要的配置之一,它不仅解决了ORM映射问题,还为复杂查询、关联查询和性能优化提供了基础。正确理解和使用ResultMap是掌握MyBatis的关键。

3.3 SQL片段(SQL Fragment)

<!-- 基础字段列表 -->
<sql id="Base_Column_List">dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark
</sql>

SQL片段的优势:

  • 复用性:避免重复编写相同的SQL片段
  • 维护性:统一管理字段列表,便于维护
  • 一致性:确保所有查询使用相同的字段顺序

4. 动态SQL详解

4.1 动态查询实现

selectDictDataList方法为例,展示动态SQL的完整实现:

<!-- 查询字典数据列表(支持动态条件) -->
<select id="selectDictDataList" parameterType="com.hn.bitao.defined.report.module.pojo.DictData" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_data<where><if test="dictCode != null">and dict_code = #{dictCode,jdbcType=BIGINT}</if><if test="dictLabel != null and dictLabel != ''">and dict_label like concat('%', #{dictLabel,jdbcType=VARCHAR}, '%')</if><if test="dictValue != null and dictValue != ''">and dict_value like concat('%', #{dictValue,jdbcType=VARCHAR}, '%')</if><if test="dictType != null and dictType != ''">and dict_type like concat('%', #{dictType,jdbcType=VARCHAR}, '%')</if><if test="status != null and status != ''">and status = #{status,jdbcType=CHAR}</if><if test="createBy != null and createBy != ''">and create_by like concat('%', #{createBy,jdbcType=VARCHAR}, '%')</if></where>order by dict_sort asc, dict_code asc
</select>
4.1.1 if标签的条件表达式
<!-- 基本条件判断 -->
<if test="name != null">AND name = #{name}</if><!-- 字符串非空判断 -->
<if test="name != null and name != ''">AND name = #{name}</if><!-- 数值比较 -->
<if test="age != null and age > 0">AND age = #{age}</if><!-- 集合判断 -->
<if test="list != null and list.size() > 0">AND id IN<foreach collection="list" item="item" open="(" separator="," close=")">#{item}</foreach>
</if><!-- 布尔值判断 -->
<if test="isActive != null and isActive">AND status = '1'</if><!-- 复杂条件组合 -->
<if test="(startDate != null and endDate != null) or keyword != null">AND (<if test="startDate != null and endDate != null">create_time BETWEEN #{startDate} AND #{endDate}</if><if test="keyword != null and keyword != ''"><if test="startDate != null and endDate != null">OR</if>(name LIKE CONCAT('%', #{keyword}, '%') OR description LIKE CONCAT('%', #{keyword}, '%'))</if>)
</if>

4.2 XML转义字符和特殊符号使用

4.2.1 > 符号在条件表达式中的使用

在 MyBatis XML 映射文件中,> 符号主要用于条件表达式中的数值比较,但由于 XML 语法的限制,需要特别注意其使用方法。

基本用法:

<!-- 数值比较 -->
<if test="age > 0">AND age = #{age}</if><!-- 价格大于指定值 -->
<if test="price > 100">AND price > #{price}</if><!-- 集合大小判断 -->
<if test="list != null and list.size() > 0">AND id IN<foreach collection="list" item="item" open="(" separator="," close=")">#{item}</foreach>
</if>
4.2.2 XML 转义字符

由于 XML 语法限制,在某些情况下需要使用转义字符:

符号转义字符说明
>&gt;大于号
<&lt;小于号
&&amp;和号
"&quot;双引号
'&apos;单引号

使用转义字符的示例:

<!-- 使用转义字符 -->
<if test="age &gt; 18">AND age > #{age}</if>
<if test="score &lt; 60">AND score < #{score}</if>
<if test="amount &gt;= 1000 &amp;&amp; amount &lt;= 10000">AND amount BETWEEN #{minAmount} AND #{maxAmount}
</if>
4.2.3 CDATA 区域

对于复杂的条件表达式,可以使用 CDATA 区域避免转义:

<!-- 使用 CDATA 区域 -->
<if test="<![CDATA[ age > 18 and score < 60 ]]>">AND age > #{age} AND score < #{score}
</if><!-- 复杂条件表达式 -->
<if test="<![CDATA[ (price > 100 && price < 1000) || (discount > 0.1 && discount < 0.5) ]]>">AND (price BETWEEN #{minPrice} AND #{maxPrice} OR discount BETWEEN #{minDiscount} AND #{maxDiscount})
</if>
4.2.4 最佳实践
  1. 简单条件:直接使用 > 符号,MyBatis 通常能正确解析
  2. 复杂条件:使用转义字符 &gt; 确保 XML 语法正确
  3. 多个比较符号:考虑使用 CDATA 区域提高可读性
  4. 避免歧义:在复杂表达式中明确使用转义字符
4.2.5 实际应用示例
<!-- 字典数据条件查询示例 -->
<select id="selectByCondition" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List" />FROM dict_data<where><if test="dictCode != null and dictCode > 0">AND dict_code = #{dictCode}</if><if test="dictSort != null and dictSort &gt; 0">AND dict_sort >= #{dictSort}</if><if test="createTime != null">AND create_time &gt;= #{createTime}</if><if test="scoreRange != null and scoreRange.size() &gt; 0">AND score BETWEEN #{scoreRange[0]} AND #{scoreRange[1]}</if><if test="<![CDATA[ status != null && (status == '0' || status == '1') ]]>">AND status = #{status}</if></where>ORDER BY dict_sort ASC, dict_code ASC
</select>
4.2.6 其他特殊字符处理

SQL 中的特殊字符:

<!-- 处理包含单引号的字符串 -->
<if test="remark != null and remark != ''">AND remark LIKE CONCAT('%', #{remark}, '%')
</if><!-- 处理包含通配符的字符串 -->
<if test="pattern != null and pattern != ''">AND field_name LIKE CONCAT('%', REPLACE(REPLACE(#{pattern}, '_', '\_'), '%', '\%'), '%') ESCAPE '\'
</if>

日期时间比较:

<!-- 日期范围查询 -->
<if test="beginTime != null and beginTime != ''">AND create_time &gt;= #{beginTime}
</if>
<if test="endTime != null and endTime != ''">AND create_time &lt;= #{endTime}
</if><!-- 使用 CDATA 的日期比较 -->
<if test="<![CDATA[ beginTime != null && endTime != null ]]>">AND create_time BETWEEN #{beginTime} AND #{endTime}
</if>

4.3 动态SQL标签详解

4.3.1 <where>标签

功能:

  • 自动添加WHERE关键字
  • 自动去除第一个AND或OR
  • 如果内部没有条件,不生成WHERE子句

示例:

<where><if test="name != null">and name = #{name}</if><if test="age != null">and age = #{age}</if>
</where>

生成的SQL:

-- 当name和age都不为空时
WHERE name = ? and age = ?-- 当只有name不为空时
WHERE name = ?-- 当都为空时
-- 不生成WHERE子句
4.3.2 <if>标签

功能:

  • 条件判断,满足条件时才包含SQL片段
  • 支持复杂的条件表达式

常用条件表达式:

<!-- 判断不为空 -->
<if test="param != null">and column = #{param}
</if><!-- 判断字符串不为空且不为空字符串 -->
<if test="param != null and param != ''">and column like concat('%', #{param}, '%')
</if><!-- 判断数字大于0 -->
<if test="param != null and param > 0">and column = #{param}
</if><!-- 判断集合不为空 -->
<if test="list != null and list.size() > 0">and column in<foreach collection="list" item="item" open="(" separator="," close=")">#{item}</foreach>
</if>
4.3.3 <choose><when><otherwise>标签

功能:

  • 类似于Java中的switch-case语句
  • 只会选择第一个满足条件的分支

示例:

<choose><when test="orderBy == 'name'">order by name</when><when test="orderBy == 'time'">order by create_time desc</when><otherwise>order by dict_sort asc</otherwise>
</choose>

复杂的choose用法:

<select id="selectByCondition" resultMap="BaseResultMap">SELECT * FROM dict_data<where><choose><when test="dictCode != null">dict_code = #{dictCode}</when><when test="dictLabel != null and dictLabel != ''">dict_label LIKE CONCAT('%', #{dictLabel}, '%')</when><when test="dictType != null and dictType != ''">dict_type = #{dictType}</when><otherwise>status = '0'</otherwise></choose></where>ORDER BY dict_sort
</select>
4.3.4 <foreach>标签

功能:

  • 遍历集合,生成重复的SQL片段
  • 常用于IN查询、批量插入等场景

示例:

<!-- IN查询 -->
<if test="ids != null and ids.size() > 0">and dict_code in<foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
</if><!-- 批量插入 -->
insert into dict_data (dict_code, dict_label, dict_value) values
<foreach collection="list" item="item" separator=",">(#{item.dictCode}, #{item.dictLabel}, #{item.dictValue})
</foreach><!-- 批量更新 -->
<foreach collection="list" item="item" separator=";">UPDATE dict_dataSET dict_label = #{item.dictLabel},dict_value = #{item.dictValue}WHERE dict_code = #{item.dictCode}
</foreach><!-- 遍历Map -->
<select id="selectByMap" resultMap="BaseResultMap">SELECT * FROM dict_data<where><foreach collection="params" index="key" item="value">AND ${key} = #{value}</foreach></where>
</select>
4.3.5 <set>标签

功能:

  • 用于UPDATE语句,自动添加SET关键字
  • 自动去除最后一个逗号

示例:

<update id="updateByPrimaryKeySelective" parameterType="DictData">update dict_data<set><if test="dictLabel != null">dict_label = #{dictLabel},</if><if test="dictValue != null">dict_value = #{dictValue},</if><if test="status != null">status = #{status},</if></set>where dict_code = #{dictCode}
</update>
4.3.6 <trim>标签

功能:

  • 去除多余的前缀、后缀或分隔符
  • 比where和set标签更灵活

示例:

<!-- 使用trim替代where -->
<select id="selectByCondition" resultMap="BaseResultMap">SELECT * FROM dict_data<trim prefix="WHERE" prefixOverrides="AND |OR "><if test="dictType != null">AND dict_type = #{dictType}</if><if test="status != null">AND status = #{status}</if><if test="dictLabel != null">OR dict_label LIKE CONCAT('%', #{dictLabel}, '%')</if></trim>
</select><!-- 动态INSERT语句 -->
<insert id="insertSelective">INSERT INTO dict_data<trim prefix="(" suffix=")" suffixOverrides=","><if test="dictLabel != null">dict_label,</if><if test="dictValue != null">dict_value,</if><if test="dictType != null">dict_type,</if>create_time</trim><trim prefix="VALUES (" suffix=")" suffixOverrides=","><if test="dictLabel != null">#{dictLabel},</if><if test="dictValue != null">#{dictValue},</if><if test="dictType != null">#{dictType},</if>NOW()</trim>
</insert>
4.3.7 <bind>标签

功能:

  • 创建变量,常用于模糊查询
  • 可以在SQL中定义和使用变量

示例:

<select id="selectByKeyword" resultMap="BaseResultMap"><bind name="pattern" value="'%' + keyword + '%'" />SELECT * FROM dict_dataWHERE dict_label LIKE #{pattern}OR dict_value LIKE #{pattern}OR remark LIKE #{pattern}
</select><!-- 复杂的bind用法 -->
<select id="selectWithComplexBind" resultMap="BaseResultMap"><bind name="startOfDay" value="startDate + ' 00:00:00'" /><bind name="endOfDay" value="endDate + ' 23:59:59'" /><bind name="searchPattern" value="'%' + (keyword != null ? keyword : '') + '%'" />SELECT * FROM dict_data<where><if test="startDate != null and endDate != null">create_time BETWEEN #{startOfDay} AND #{endOfDay}</if><if test="keyword != null and keyword != ''">AND (dict_label LIKE #{searchPattern} OR dict_value LIKE #{searchPattern})</if></where>
</select>

4.4 参数传递方式

4.4.1 单个参数
// Mapper接口
DictData selectByPrimaryKey(Long dictCode);
<!-- XML映射 -->
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_datawhere dict_code = #{dictCode,jdbcType=BIGINT}
</select>
4.4.2 对象参数
// Mapper接口
List<DictData> selectDictDataList(DictData dictData);
<!-- XML映射 -->
<select id="selectDictDataList" parameterType="DictData" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_data<where><if test="dictType != null and dictType != ''">and dict_type = #{dictType}</if></where>
</select>
4.4.3 多个参数(使用@Param注解)
// Mapper接口
List<DictData> selectByTypeAndStatus(@Param("dictType") String dictType, @Param("status") String status);
<!-- XML映射 -->
<select id="selectByTypeAndStatus" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_datawhere dict_type = #{dictType,jdbcType=VARCHAR}and status = #{status,jdbcType=CHAR}
</select>
4.4.4 Map参数
// Mapper接口
List<DictData> selectByMap(Map<String, Object> params);
<!-- XML映射 -->
<select id="selectByMap" parameterType="map" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_data<where><if test="dictType != null">and dict_type = #{dictType}</if><if test="status != null">and status = #{status}</if></where>
</select>

5. CRUD操作实现

5.1 新增操作

5.1.1 完整新增
<insert id="insert" parameterType="DictData">insert into dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type,css_class, list_class, is_default, status, create_by,create_time, update_by, update_time, remark) values (#{dictCode,jdbcType=BIGINT}, #{dictSort,jdbcType=INTEGER},#{dictLabel,jdbcType=VARCHAR}, #{dictValue,jdbcType=VARCHAR},#{dictType,jdbcType=VARCHAR}, #{cssClass,jdbcType=VARCHAR},#{listClass,jdbcType=VARCHAR}, #{isDefault,jdbcType=CHAR},#{status,jdbcType=CHAR}, #{createBy,jdbcType=VARCHAR},#{createTime,jdbcType=TIMESTAMP}, #{updateBy,jdbcType=VARCHAR},#{updateTime,jdbcType=TIMESTAMP}, #{remark,jdbcType=VARCHAR})
</insert>
5.1.2 选择性新增
<insert id="insertSelective" parameterType="DictData">insert into dict_data<trim prefix="(" suffix=")" suffixOverrides=","><if test="dictCode != null">dict_code,</if><if test="dictSort != null">dict_sort,</if><if test="dictLabel != null">dict_label,</if><!-- 其他字段... --></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="dictCode != null">#{dictCode,jdbcType=BIGINT},</if><if test="dictSort != null">#{dictSort,jdbcType=INTEGER},</if><if test="dictLabel != null">#{dictLabel,jdbcType=VARCHAR},</if><!-- 其他字段... --></trim>
</insert>

<trim>标签说明:

  • prefix:在内容前添加指定字符串
  • suffix:在内容后添加指定字符串
  • suffixOverrides:去除内容末尾的指定字符串
  • prefixOverrides:去除内容开头的指定字符串

5.2 删除操作

<!-- 根据主键删除 -->
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">delete from dict_datawhere dict_code = #{dictCode,jdbcType=BIGINT}
</delete><!-- 根据条件批量删除 -->
<delete id="deleteByIds" parameterType="java.util.List">delete from dict_datawhere dict_code in<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</delete>

5.3 修改操作

5.3.1 完整更新
<update id="updateByPrimaryKey" parameterType="DictData">update dict_dataset dict_sort = #{dictSort,jdbcType=INTEGER},dict_label = #{dictLabel,jdbcType=VARCHAR},dict_value = #{dictValue,jdbcType=VARCHAR},dict_type = #{dictType,jdbcType=VARCHAR},css_class = #{cssClass,jdbcType=VARCHAR},list_class = #{listClass,jdbcType=VARCHAR},is_default = #{isDefault,jdbcType=CHAR},status = #{status,jdbcType=CHAR},update_by = #{updateBy,jdbcType=VARCHAR},update_time = #{updateTime,jdbcType=TIMESTAMP},remark = #{remark,jdbcType=VARCHAR}where dict_code = #{dictCode,jdbcType=BIGINT}
</update>
5.3.2 选择性更新
<update id="updateByPrimaryKeySelective" parameterType="DictData">update dict_data<set><if test="dictSort != null">dict_sort = #{dictSort,jdbcType=INTEGER},</if><if test="dictLabel != null">dict_label = #{dictLabel,jdbcType=VARCHAR},</if><if test="dictValue != null">dict_value = #{dictValue,jdbcType=VARCHAR},</if><!-- 其他字段... --></set>where dict_code = #{dictCode,jdbcType=BIGINT}
</update>

5.4 查询操作

5.4.1 简单查询
<!-- 根据主键查询 -->
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_datawhere dict_code = #{dictCode,jdbcType=BIGINT}
</select><!-- 查询所有 -->
<select id="selectAll" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_dataorder by dict_sort asc
</select>
5.4.2 条件查询
<!-- 根据字典类型查询 -->
<select id="selectByDictType" parameterType="java.lang.String" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_datawhere dict_type = #{dictType,jdbcType=VARCHAR}order by dict_sort asc
</select>
5.4.3 统计查询
<!-- 统计总数 -->
<select id="countAll" resultType="java.lang.Integer">select count(*) from dict_data
</select><!-- 根据类型统计 -->
<select id="countByDictType" parameterType="java.lang.String" resultType="java.lang.Integer">select count(*)from dict_datawhere dict_type = #{dictType,jdbcType=VARCHAR}
</select>

6. 高级特性

6.1 关联查询

6.1.1 一对一关联
<!-- 字典数据关联字典类型 -->
<resultMap id="DictDataWithTypeResult" type="DictData"><id property="dictCode" column="dict_code" /><result property="dictLabel" column="dict_label" /><result property="dictValue" column="dict_value" /><result property="dictType" column="dict_type" /><!-- 关联字典类型信息 --><association property="dictTypeInfo" javaType="DictType"><id property="dictId" column="dict_id" /><result property="dictName" column="dict_name" /><result property="dictType" column="dict_type" /></association>
</resultMap><select id="selectWithDictType" resultMap="DictDataWithTypeResult">select d.dict_code, d.dict_label, d.dict_value, d.dict_type,t.dict_id, t.dict_name, t.dict_type as type_codefrom dict_data dleft join dict_type t on d.dict_type = t.dict_typewhere d.dict_code = #{dictCode}
</select>
6.1.2 一对多关联
<!-- 字典类型关联多个字典数据 -->
<resultMap id="DictTypeWithDataResult" type="DictType"><id property="dictId" column="dict_id" /><result property="dictName" column="dict_name" /><result property="dictType" column="dict_type" /><!-- 关联多个字典数据 --><collection property="dictDataList" ofType="DictData"><id property="dictCode" column="data_dict_code" /><result property="dictLabel" column="data_dict_label" /><result property="dictValue" column="data_dict_value" /></collection>
</resultMap><select id="selectTypeWithData" resultMap="DictTypeWithDataResult">select t.dict_id, t.dict_name, t.dict_type,d.dict_code as data_dict_code,d.dict_label as data_dict_label,d.dict_value as data_dict_valuefrom dict_type tleft join dict_data d on t.dict_type = d.dict_typewhere t.dict_id = #{dictId}
</select>

6.2 分页查询支持

<!-- 分页查询(配合PageHelper使用) -->
<select id="selectForPage" parameterType="DictData" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_data<where><if test="dictType != null and dictType != ''">and dict_type like concat('%', #{dictType}, '%')</if><if test="status != null and status != ''">and status = #{status}</if></where>order by dict_sort asc, dict_code asc
</select>

6.3 批量操作

6.3.1 批量插入
<insert id="batchInsert" parameterType="java.util.List">insert into dict_data (dict_code, dict_label, dict_value, dict_type, status) values<foreach collection="list" item="item" separator=",">(#{item.dictCode}, #{item.dictLabel}, #{item.dictValue},#{item.dictType}, #{item.status})</foreach>
</insert>
6.3.2 批量更新
<update id="batchUpdate" parameterType="java.util.List"><foreach collection="list" item="item" separator=";">update dict_dataset dict_label = #{item.dictLabel},dict_value = #{item.dictValue},status = #{item.status}where dict_code = #{item.dictCode}</foreach>
</update>

7. 性能优化

7.1 SQL优化原则

7.1.1 避免SELECT *
<!-- ❌ 错误示例 -->
<select id="selectAll" resultType="DictData">select * from dict_data
</select><!-- ✅ 正确示例 -->
<select id="selectAll" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_data
</select>
7.1.2 合理使用索引
<!-- 在经常查询的字段上建立索引 -->
<select id="selectByDictType" parameterType="java.lang.String" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_datawhere dict_type = #{dictType,jdbcType=VARCHAR}  -- dict_type字段应建立索引order by dict_sort asc
</select>
7.1.3 分页查询优化
<!-- 使用LIMIT进行分页(MySQL) -->
<select id="selectByPage" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_data<where><if test="dictType != null and dictType != ''">and dict_type = #{dictType}</if></where>order by dict_sort asclimit #{offset}, #{pageSize}
</select>

7.2 缓存配置

7.2.1 一级缓存(默认开启)
<!-- 一级缓存在同一个SqlSession中有效 -->
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_datawhere dict_code = #{dictCode,jdbcType=BIGINT}
</select>
7.2.2 二级缓存配置
<!-- 在mapper文件中开启二级缓存 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/><!-- 或者使用自定义缓存 -->
<cache type="com.example.cache.MyCache"/><!-- 在查询语句中使用缓存 -->
<select id="selectByDictType" parameterType="java.lang.String" resultMap="BaseResultMap" useCache="true">select <include refid="Base_Column_List" />from dict_datawhere dict_type = #{dictType,jdbcType=VARCHAR}order by dict_sort asc
</select>

8. 最佳实践

8.1 命名规范

8.1.1 文件命名
  • Mapper接口:XxxMapper.java
  • XML映射文件:XxxMapper.xml
  • 位置:src/main/resources/mapper/
8.1.2 方法命名
// 查询操作
selectByPrimaryKey()     // 根据主键查询
selectByXxx()           // 根据条件查询
selectXxxList()         // 查询列表
countByXxx()           // 统计数量// 新增操作
insert()               // 完整新增
insertSelective()      // 选择性新增
batchInsert()          // 批量新增// 修改操作
updateByPrimaryKey()   // 根据主键更新
updateByPrimaryKeySelective()  // 选择性更新
batchUpdate()          // 批量更新// 删除操作
deleteByPrimaryKey()   // 根据主键删除
deleteByXxx()         // 根据条件删除
batchDelete()         // 批量删除

8.2 参数校验

<!-- 在SQL中进行参数校验 -->
<select id="selectDictDataList" parameterType="DictData" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from dict_data<where><!-- 字符串参数校验:不为null且不为空字符串 --><if test="dictType != null and dictType != ''">and dict_type = #{dictType,jdbcType=VARCHAR}</if><!-- 数字参数校验:不为null且大于0 --><if test="dictCode != null and dictCode > 0">and dict_code = #{dictCode,jdbcType=BIGINT}</if><!-- 集合参数校验:不为null且不为空 --><if test="statusList != null and statusList.size() > 0">and status in<foreach collection="statusList" item="status" open="(" separator="," close=")">#{status}</foreach></if></where>order by dict_sort asc
</select>

8.3 错误处理

// 在Service层处理Mapper异常
@Service
public class DictDataServiceImpl implements DictDataService {@Autowiredprivate DictDataMapper dictDataMapper;@Overridepublic DictData selectDictDataById(Long dictCode) {try {if (dictCode == null || dictCode <= 0) {throw new IllegalArgumentException("字典编码不能为空或小于等于0");}return dictDataMapper.selectByPrimaryKey(dictCode);} catch (Exception e) {log.error("查询字典数据失败,dictCode: {}", dictCode, e);throw new BusinessException("查询字典数据失败");}}
}

8.4 日志配置

<!-- 在logback.xml中配置MyBatis日志 -->
<logger name="com.hn.bitao.defined.report.module.mapper" level="DEBUG" /><!-- 或者在application.yml中配置 -->
logging:level:com.hn.bitao.defined.report.module.mapper: DEBUG# 显示SQL语句org.apache.ibatis.logging.LogFactory: DEBUG

9. 常见问题与解决方案

9.1 常见错误

9.1.1 Mapper接口找不到

错误信息:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

解决方案:

  1. 检查XML文件的namespace是否与接口完整类名一致
  2. 检查方法名是否与XML中的id一致
  3. 检查XML文件是否在正确的位置
  4. 检查Maven是否正确编译了XML文件
9.1.2 参数映射错误

错误信息:

org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'xxx'

解决方案:

  1. 检查实体类是否有对应的getter/setter方法
  2. 检查参数名是否正确
  3. 使用@Param注解明确指定参数名
9.1.3 结果映射错误

错误信息:

org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'xxx' from result set

解决方案:

  1. 检查ResultMap中的column名是否与SQL查询结果一致
  2. 检查数据库字段名是否正确
  3. 使用别名确保字段名匹配

9.2 性能问题

9.2.1 N+1查询问题

问题描述:

// 查询所有字典数据
List<DictData> dataList = dictDataMapper.selectAll();
// 为每个字典数据查询类型信息(产生N+1查询)
for (DictData data : dataList) {DictType type = dictTypeMapper.selectByType(data.getDictType());data.setDictTypeInfo(type);
}

解决方案:

<!-- 使用关联查询一次性获取所有数据 -->
<select id="selectAllWithType" resultMap="DictDataWithTypeResult">select d.dict_code, d.dict_label, d.dict_value, d.dict_type,t.dict_id, t.dict_namefrom dict_data dleft join dict_type t on d.dict_type = t.dict_type
</select>
9.2.2 大数据量查询优化

问题: 一次性查询大量数据导致内存溢出

解决方案:

<!-- 使用游标查询 -->
<select id="selectLargeData" resultMap="BaseResultMap" fetchSize="1000">select <include refid="Base_Column_List" />from dict_dataorder by dict_code
</select>
// 在Service中使用游标
@Override
public void processLargeData() {try (SqlSession sqlSession = sqlSessionFactory.openSession()) {DictDataMapper mapper = sqlSession.getMapper(DictDataMapper.class);try (Cursor<DictData> cursor = mapper.selectLargeData()) {for (DictData data : cursor) {// 处理每条数据processData(data);}}}
}

10. MyBatis注解方式

10.1 注解Mapper概述

除了XML映射文件,MyBatis还支持使用注解的方式定义SQL语句,适用于简单的CRUD操作。

10.2 基本注解

10.2.1 查询注解
public interface DictDataMapper {// @Select 注解@Select("SELECT * FROM dict_data WHERE dict_code = #{dictCode}")DictData selectByPrimaryKey(Long dictCode);// 使用 @Results 进行结果映射@Select("SELECT dict_code, dict_label, dict_value FROM dict_data WHERE dict_type = #{dictType}")@Results({@Result(property = "dictCode", column = "dict_code", id = true),@Result(property = "dictLabel", column = "dict_label"),@Result(property = "dictValue", column = "dict_value")})List<DictData> selectByDictType(String dictType);// 复杂查询使用 @SelectProvider@SelectProvider(type = DictDataSqlProvider.class, method = "selectDictDataList")List<DictData> selectDictDataList(DictData dictData);
}
10.2.2 插入注解
public interface DictDataMapper {// @Insert 注解@Insert("INSERT INTO dict_data(dict_label, dict_value, dict_type) VALUES(#{dictLabel}, #{dictValue}, #{dictType})")@Options(useGeneratedKeys = true, keyProperty = "dictCode")int insert(DictData dictData);// 批量插入使用 @InsertProvider@InsertProvider(type = DictDataSqlProvider.class, method = "batchInsert")int batchInsert(@Param("list") List<DictData> dataList);
}
10.2.3 更新注解
public interface DictDataMapper {// @Update 注解@Update("UPDATE dict_data SET dict_label = #{dictLabel}, dict_value = #{dictValue} WHERE dict_code = #{dictCode}")int updateByPrimaryKey(DictData dictData);// 动态更新使用 @UpdateProvider@UpdateProvider(type = DictDataSqlProvider.class, method = "updateSelective")int updateByPrimaryKeySelective(DictData dictData);
}
10.2.4 删除注解
public interface DictDataMapper {// @Delete 注解@Delete("DELETE FROM dict_data WHERE dict_code = #{dictCode}")int deleteByPrimaryKey(Long dictCode);// 批量删除@Delete("<script>" +"DELETE FROM dict_data WHERE dict_code IN " +"<foreach collection='ids' item='id' open='(' separator=',' close=')'>" +"#{id}" +"</foreach>" +"</script>")int deleteByIds(@Param("ids") List<Long> ids);
}

10.3 SQL提供者类

public class DictDataSqlProvider {public String selectDictDataList(DictData dictData) {return new SQL() {{SELECT("dict_code, dict_label, dict_value, dict_type, status");FROM("dict_data");if (dictData.getDictType() != null && !dictData.getDictType().isEmpty()) {WHERE("dict_type LIKE CONCAT('%', #{dictType}, '%')");}if (dictData.getStatus() != null && !dictData.getStatus().isEmpty()) {WHERE("status = #{status}");}ORDER_BY("dict_sort ASC, dict_code ASC");}}.toString();}public String updateSelective(DictData dictData) {return new SQL() {{UPDATE("dict_data");if (dictData.getDictLabel() != null) {SET("dict_label = #{dictLabel}");}if (dictData.getDictValue() != null) {SET("dict_value = #{dictValue}");}if (dictData.getStatus() != null) {SET("status = #{status}");}WHERE("dict_code = #{dictCode}");}}.toString();}public String batchInsert(@Param("list") List<DictData> dataList) {StringBuilder sql = new StringBuilder();sql.append("INSERT INTO dict_data (dict_label, dict_value, dict_type) VALUES ");for (int i = 0; i < dataList.size(); i++) {if (i > 0) {sql.append(", ");}sql.append("(#{list[").append(i).append("].dictLabel}, ").append("#{list[").append(i).append("].dictValue}, ").append("#{list[").append(i).append("].dictType})")}return sql.toString();}
}

10.4 注解与XML混合使用

@Mapper
public interface DictDataMapper {// 简单查询使用注解@Select("SELECT * FROM dict_data WHERE dict_code = #{dictCode}")DictData selectByPrimaryKey(Long dictCode);// 复杂查询使用XMLList<DictData> selectDictDataList(DictData dictData);// 简单插入使用注解@Insert("INSERT INTO dict_data(dict_label, dict_value) VALUES(#{dictLabel}, #{dictValue})")int insert(DictData dictData);// 复杂更新使用XMLint updateByPrimaryKeySelective(DictData dictData);
}

11. 高级语法特性

11.1 类型处理器(TypeHandler)

11.1.1 自定义类型处理器
// 枚举类型处理器
public class StatusTypeHandler extends BaseTypeHandler<StatusEnum> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, StatusEnum parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, parameter.getCode());}@Overridepublic StatusEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {String code = rs.getString(columnName);return StatusEnum.getByCode(code);}@Overridepublic StatusEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String code = rs.getString(columnIndex);return StatusEnum.getByCode(code);}@Overridepublic StatusEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String code = cs.getString(columnIndex);return StatusEnum.getByCode(code);}
}
11.1.2 类型处理器配置
<!-- 在 mybatis-config.xml 中注册类型处理器 -->
<typeHandlers><typeHandler handler="com.example.handler.StatusTypeHandler" javaType="com.example.enums.StatusEnum" jdbcType="VARCHAR"/>
</typeHandlers><!-- 在 ResultMap 中使用类型处理器 -->
<resultMap id="BaseResultMap" type="DictData"><result property="status" column="status" typeHandler="com.example.handler.StatusTypeHandler"/>
</resultMap><!-- 在参数中使用类型处理器 -->
<insert id="insert" parameterType="DictData">INSERT INTO dict_data (status) VALUES (#{status, typeHandler=com.example.handler.StatusTypeHandler})
</insert>

11.2 插件机制(Plugin)

11.2.1 自定义插件
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlLogPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs()[1];// 记录SQL执行日志long startTime = System.currentTimeMillis();Object result = invocation.proceed();long endTime = System.currentTimeMillis();log.info("SQL执行时间: {}ms, 方法: {}", (endTime - startTime), mappedStatement.getId());return result;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {// 设置插件属性}
}
11.2.2 插件配置
<!-- 在 mybatis-config.xml 中配置插件 -->
<plugins><plugin interceptor="com.example.plugin.SqlLogPlugin"><property name="logLevel" value="INFO"/></plugin>
</plugins>

11.3 存储过程调用

11.3.1 简单存储过程
<!-- 调用存储过程 -->
<select id="callProcedure" parameterType="map" statementType="CALLABLE">{call get_dict_data_count(#{dictType, mode=IN, jdbcType=VARCHAR}, #{count, mode=OUT, jdbcType=INTEGER})}
</select>
// Java调用
public int getDictDataCount(String dictType) {Map<String, Object> params = new HashMap<>();params.put("dictType", dictType);dictDataMapper.callProcedure(params);return (Integer) params.get("count");
}
11.3.2 复杂存储过程
<select id="callComplexProcedure" parameterType="map" statementType="CALLABLE">{call complex_dict_procedure(#{dictType, mode=IN, jdbcType=VARCHAR},#{status, mode=IN, jdbcType=CHAR},#{resultList, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=BaseResultMap})}
</select>

11.4 多数据源支持

11.4.1 数据源注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {String value() default "master";
}
11.4.2 使用多数据源
@Mapper
public interface DictDataMapper {// 使用主数据源(默认)@DataSource("master")List<DictData> selectDictDataList(DictData dictData);// 使用从数据源@DataSource("slave")List<DictData> selectForReport(DictData dictData);// 使用归档数据源@DataSource("archive")List<DictData> selectArchiveData(DictData dictData);
}

11.5 自定义SQL构建器

11.5.1 SQL构建器类
public class DictDataSqlBuilder {public String buildSelectSql(DictData dictData) {StringBuilder sql = new StringBuilder();sql.append("SELECT dict_code, dict_label, dict_value, dict_type, status ");sql.append("FROM dict_data WHERE 1=1 ");if (dictData.getDictType() != null && !dictData.getDictType().isEmpty()) {sql.append("AND dict_type LIKE CONCAT('%', #{dictType}, '%') ");}if (dictData.getStatus() != null && !dictData.getStatus().isEmpty()) {sql.append("AND status = #{status} ");}if (dictData.getDictLabel() != null && !dictData.getDictLabel().isEmpty()) {sql.append("AND dict_label LIKE CONCAT('%', #{dictLabel}, '%') ");}sql.append("ORDER BY dict_sort ASC, dict_code ASC");return sql.toString();}public String buildUpdateSql(DictData dictData) {StringBuilder sql = new StringBuilder();sql.append("UPDATE dict_data SET ");List<String> sets = new ArrayList<>();if (dictData.getDictLabel() != null) {sets.add("dict_label = #{dictLabel}");}if (dictData.getDictValue() != null) {sets.add("dict_value = #{dictValue}");}if (dictData.getStatus() != null) {sets.add("status = #{status}");}sql.append(String.join(", ", sets));sql.append(" WHERE dict_code = #{dictCode}");return sql.toString();}
}
11.5.2 使用SQL构建器
@Mapper
public interface DictDataMapper {@SelectProvider(type = DictDataSqlBuilder.class, method = "buildSelectSql")List<DictData> selectByCondition(DictData dictData);@UpdateProvider(type = DictDataSqlBuilder.class, method = "buildUpdateSql")int updateSelective(DictData dictData);
}

12. 总结

10.1 Mapper层设计原则

  1. 单一职责:每个Mapper只负责一个实体的数据访问
  2. 接口分离:将复杂查询拆分为多个简单方法
  3. 命名规范:使用统一的命名规范,提高代码可读性
  4. 参数校验:在SQL层面进行基础参数校验
  5. 性能优化:合理使用索引、分页、缓存等优化手段

10.2 开发建议

  1. 优先使用动态SQL:提高SQL的灵活性和复用性
  2. 合理使用ResultMap:避免字段映射错误
  3. 注意SQL注入防护:始终使用参数化查询
  4. 适当使用缓存:对于查询频繁且变化较少的数据
  5. 监控SQL性能:定期检查慢查询并优化

10.3 学习路径

  1. 基础学习:掌握MyBatis基本概念和配置
  2. 实践练习:通过项目实践掌握CRUD操作
  3. 进阶学习:学习动态SQL、关联查询、性能优化
  4. 深入研究:了解MyBatis源码和高级特性
  5. 最佳实践:总结项目经验,形成开发规范

文档版本: v1.0
创建时间: 2025-01-27
适用项目: bitao-defined-report
维护人员: 开发团队


文章转载自:

http://y1Mp4Lgg.zbmcz.cn
http://hkeEeIgm.zbmcz.cn
http://RIt4KhMe.zbmcz.cn
http://f54YAaFp.zbmcz.cn
http://92RspCj9.zbmcz.cn
http://PqL1FnI1.zbmcz.cn
http://uZDY7CYc.zbmcz.cn
http://Myoum1ZV.zbmcz.cn
http://7XP0a8VM.zbmcz.cn
http://mtXloSy3.zbmcz.cn
http://V4CPykaO.zbmcz.cn
http://Jr9sl8bp.zbmcz.cn
http://sJysGAJC.zbmcz.cn
http://8DXqwRvX.zbmcz.cn
http://m7LLaAeJ.zbmcz.cn
http://xFuPZJta.zbmcz.cn
http://Ocj1tWU8.zbmcz.cn
http://hCop7pPd.zbmcz.cn
http://pxNXo84b.zbmcz.cn
http://3AMRCmoi.zbmcz.cn
http://YSKFFKji.zbmcz.cn
http://MuEujhxC.zbmcz.cn
http://i2wssc7t.zbmcz.cn
http://oTwxQcO1.zbmcz.cn
http://UfnGStlt.zbmcz.cn
http://FC3AJEIF.zbmcz.cn
http://Y9eNzdz4.zbmcz.cn
http://ySV7HNCQ.zbmcz.cn
http://Ld3OEXMB.zbmcz.cn
http://p8GHRcDP.zbmcz.cn
http://www.dtcms.com/a/367678.html

相关文章:

  • 设计五种算法精确的身份证号匹配
  • JVM参数调优(GC 回收器 选择)
  • vue3入门- script setup详解下
  • MySQL命令--备份和恢复数据库的Shell脚本
  • 因为对象装箱拆箱导致的空指针异常
  • 济南矩阵跃动完成千万融资!国产GEO工具能否挑战国际巨头?
  • 【Linux基础】Linux文件系统深度解析:EXT4与XFS技术详解与应用
  • Opencv: cv::LUT()深入解析图像块快速查表变换
  • 【FPGA】单总线——DS18B20
  • 安装VScode和nodeJS
  • 【SuperSocket 】SuperSocket 中自定义 Session
  • 【涂鸦T5】6. lvgl显示光感数值
  • 【CS32L015C8T6】配置单片机PWM输出(内附完整代码及注释)
  • 华为校招实习留学生机试全攻略:真题目录+算法分类+在线OJ+备考策略
  • 【机器学习】HanLP+Weka+Java=Random Forest算法模型
  • Photoshop - Photoshop 触摸功能
  • Java Web :技术根基与产业实践的多维耦合
  • 在树莓派集群上部署 Distributed Llama (Qwen 3 14B) 详细指南
  • 解析PE文件的导入表和导出表
  • Flutter 3.35.2 以上版本中 数字转字符串的方法指南
  • 跨平台RTSP|RTMP|GB28181推拉流端智能录像模块技术探究
  • “人工智能+”的新范式:应用赋能与风险应对
  • 聚焦GISBox矢量服务:数据管理、数据库连接与框架预览全攻略
  • 如何避免 “空的 Windows 宿主机目录” 挂载时覆盖容器内的重要目录文件(导致容器关键文件丢失、无法启动)
  • wins中怎么用一个bat文件启动jar包和tomcat等多个服务
  • 深入理解awk
  • Leetcode 206. 反转链表 迭代/递归
  • RFID+工业互联网:汽车全景天幕全生命周期智能检测体系构建
  • javaweb基础第一天总结(HTML-CSS)
  • Django全局异常处理全攻略