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

MyBatis持久层实现

MyBatis持久层实现

package com.example.usermanagement.mapper;import com.example.usermanagement.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;/*** 用户Mapper接口* @Mapper: 标识这是MyBatis的Mapper接口*/
@Mapper
public interface UserMapper {// 插入用户int insert(User user);// 根据ID删除用户int deleteById(Long id);// 更新用户信息int update(User user);// 根据ID查询用户User selectById(Long id);// 根据用户名查询用户User selectByUsername(String username);// 查询所有用户List<User> selectAll();// 分页查询用户List<User> selectByPage(@Param("offset") Integer offset,@Param("limit") Integer limit);// 统计用户总数int count();/*** 根据邮箱查询用户* @param email 邮箱地址* @return 用户信息*/User selectByEmail(String email);List<User> searchByUsername(@Param("username") String username,@Param("offset") int offset,@Param("limit") int limit);int countByUsername(@Param("username") String username);
}
<?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.usermanagement.mapper.UserMapper"><!-- 结果映射:定义数据库字段与实体类属性的映射关系 --><resultMap id="BaseResultMap" type="com.example.usermanagement.entity.User"><id column="id" property="id"/><result column="username" property="username"/><result column="password" property="password"/><result column="email" property="email"/><result column="phone" property="phone"/><result column="status" property="status"/><result column="score" property="score"/><result column="create_time" property="createTime"/><result column="update_time" property="updateTime"/></resultMap><!-- 基础字段列表 --><sql id="Base_Column_List">id, username, password, email, phone, status, score, create_time, update_time</sql><!-- 插入用户 --><!-- 原来的写法 --><insert id="insert" parameterType="com.example.usermanagement.entity.User"useGeneratedKeys="true" keyProperty="id">INSERT INTO user (username, password, email, phone, status, score)VALUES (#{username}, #{password}, #{email}, #{phone}, #{status}, #{score})</insert><!-- 根据ID删除 --><delete id="deleteById" parameterType="Long">DELETE FROM user WHERE id = #{id}</delete><!-- 更新用户 --><update id="update" parameterType="com.example.usermanagement.entity.User">UPDATE user<set><if test="username != null">username = #{username},</if><if test="password != null">password = #{password},</if><if test="email != null">email = #{email},</if><if test="phone != null">phone = #{phone},</if><if test="status != null">status = #{status},</if><if test="score != null">score = #{score},</if></set>WHERE id = #{id}</update><!-- 根据ID查询 --><select id="selectById" parameterType="Long" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userWHERE id = #{id}</select><!-- 根据用户名查询 --><select id="selectByUsername" parameterType="String" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userWHERE username = #{username}</select><!-- 查询所有用户 --><select id="selectAll" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userORDER BY id DESC</select><!-- 分页查询 --><select id="selectByPage" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userORDER BY id DESCLIMIT #{offset}, #{limit}</select><!-- 统计总数 --><select id="count" resultType="int">SELECT COUNT(*) FROM user</select><!-- 根据邮箱查询用户 --><select id="selectByEmail" parameterType="String" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userWHERE email = #{email}</select><!-- 根据用户名搜索(模糊查询) --><select id="searchByUsername" resultMap="BaseResultMap">SELECT<include refid="Base_Column_List" />FROM userWHERE username LIKE CONCAT('%', #{username}, '%')ORDER BY id DESCLIMIT #{offset}, #{limit}</select><!-- 统计搜索结果数量 --><select id="countByUsername" resultType="int">SELECT COUNT(*)FROM userWHERE username LIKE CONCAT('%', #{username}, '%')</select></mapper>

1. Mapper接口设计分析

1.1 接口声明与注解

@Mapper
public interface UserMapper {// 方法定义
}

@Mapper注解详解:

  • MyBatis标识:告诉Spring这是MyBatis的Mapper接口
  • 自动代理:Spring自动创建接口的实现类
  • 依赖注入:可以被@Autowired注入到Service中
  • 类型安全:编译时检查方法签名

接口vs实现类:

// 传统DAO实现
public class UserDaoImpl implements UserDao {// 需要手写JDBC代码
}// MyBatis Mapper
public interface UserMapper {// 只需要定义方法签名,XML中写SQL
}

1.2 方法命名规范

// 查询类方法
User selectById(Long id);
User selectByUsername(String username);
List<User> selectAll();// 插入类方法
int insert(User user);// 更新类方法
int update(User user);// 删除类方法
int deleteById(Long id);// 统计类方法
int count();

命名约定分析:

  • select:查询操作,返回实体或集合
  • insert:插入操作,返回影响行数
  • update:更新操作,返回影响行数
  • delete:删除操作,返回影响行数
  • count:统计操作,返回数量

1.3 参数传递设计

// 单个参数(MyBatis自动处理)
User selectById(Long id);// 多个参数(使用@Param注解)
List<User> selectByPage(@Param("offset") Integer offset,@Param("limit") Integer limit);// 复杂对象参数
int insert(User user);

@Param注解作用:

  • 参数命名:在XML中可以通过名称引用参数
  • 多参数支持:避免MyBatis的参数0、参数1命名
  • 可读性增强:XML中的参数名更有意义

2. XML映射文件结构解析

2.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">

作用说明:

  • XML声明:指定版本和编码
  • DTD约束:定义XML文件的结构规范
  • MyBatis验证:确保XML语法正确

2.2 命名空间配置

<mapper namespace="com.example.usermanagement.mapper.UserMapper">

namespace重要性:

  • 接口绑定:必须与Mapper接口全限定名一致
  • 方法映射:XML中的SQL语句与接口方法一一对应
  • 避免冲突:不同Mapper的同名方法不会冲突

3. 结果映射深度解析

3.1 ResultMap配置

<resultMap id="BaseResultMap" type="com.example.usermanagement.entity.User"><id column="id" property="id"/><result column="username" property="username"/><result column="password" property="password"/><result column="email" property="email"/><result column="phone" property="phone"/><result column="status" property="status"/><result column="score" property="score"/><result column="create_time" property="createTime"/><result column="update_time" property="updateTime"/>
</resultMap>

ResultMap核心作用:

字段映射:

  • 数据库字段Java属性
  • create_timecreateTime(下划线转驼峰)
  • update_timeupdateTime

标签区别:

  • <id>:主键字段,MyBatis优化处理
  • <result>:普通字段映射

类型映射:

type="com.example.usermanagement.entity.User"
  • 指定返回的Java对象类型
  • MyBatis自动创建对象并设置属性

3.2 字段列表复用

<sql id="Base_Column_List">id, username, password, email, phone, status, score, create_time, update_time
</sql>

设计优势:

  • DRY原则:避免重复定义字段列表
  • 维护性:字段变更只需修改一处
  • 可读性:SQL语句更简洁

使用方式:

<select id="selectById" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userWHERE id = #{id}
</select>

4. SQL语句详细分析

4.1 插入语句设计

<insert id="insert" parameterType="com.example.usermanagement.entity.User"useGeneratedKeys="true" keyProperty="id">INSERT INTO user (username, password, email, phone, status, score)VALUES (#{username}, #{password}, #{email}, #{phone}, #{status}, #{score})
</insert>

核心配置解析:

useGeneratedKeys=“true”:

  • 自动生成主键:数据库自增ID
  • 回写主键:插入后ID自动设置到对象
  • 便于后续操作:可以直接使用生成的ID

keyProperty=“id”:

  • 指定主键属性:告诉MyBatis将生成的主键值设置给哪个属性
  • 对象更新:插入后User对象的id字段会被自动设置

参数绑定:

#{username}, #{password}, #{email}
  • 预编译SQL:防止SQL注入
  • 类型转换:自动处理Java类型到数据库类型的转换
  • 空值处理:null值自动处理

使用效果:

User user = new User();
user.setUsername("testuser");
user.setPassword("123456");userMapper.insert(user);
// 插入后,user.getId() 会自动获得生成的主键值
System.out.println("生成的ID: " + user.getId());

4.2 动态更新语句

<update id="update" parameterType="com.example.usermanagement.entity.User">UPDATE user<set><if test="username != null">username = #{username},</if><if test="password != null">password = #{password},</if><if test="email != null">email = #{email},</if><if test="phone != null">phone = #{phone},</if><if test="status != null">status = #{status},</if><if test="score != null">score = #{score},</if></set>WHERE id = #{id}
</update>

动态SQL优势:

<set>标签作用:

  • 智能组装:自动处理SET子句
  • 逗号处理:自动去除末尾多余的逗号
  • 条件更新:只更新有值的字段

<if>条件判断:

<if test="username != null">username = #{username},</if>
  • 空值检查:只有非null字段才会被更新
  • 灵活更新:支持部分字段更新
  • 避免覆盖:不会将现有数据置为null

生成的SQL示例:

-- 如果只更新用户名和邮箱
UPDATE user SET username = ?, email = ? WHERE id = ?-- 如果只更新状态
UPDATE user SET status = ? WHERE id = ?

4.3 分页查询实现

<select id="selectByPage" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userORDER BY id DESCLIMIT #{offset}, #{limit}
</select>

MySQL分页语法:

  • LIMIT offset, limit:MySQL特有语法
  • offset:跳过的记录数
  • limit:返回的记录数

分页计算:

// 第1页,每页10条:LIMIT 0, 10
// 第2页,每页10条:LIMIT 10, 10
// 第3页,每页10条:LIMIT 20, 10
Integer offset = (pageNum - 1) * pageSize;

排序策略:

ORDER BY id DESC
  • ID倒序:最新数据在前
  • 稳定排序:确保分页结果一致性

4.4 模糊搜索实现

<select id="searchByUsername" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userWHERE username LIKE CONCAT('%', #{username}, '%')ORDER BY id DESCLIMIT #{offset}, #{limit}
</select>

LIKE查询分析:

CONCAT函数:

  • 字符串拼接:MySQL的CONCAT函数
  • 防止SQL注入:参数化查询
  • 跨数据库:不同数据库有不同语法

不同数据库的写法:

<!-- MySQL -->
WHERE username LIKE CONCAT('%', #{username}, '%')<!-- Oracle -->
WHERE username LIKE '%' || #{username} || '%'<!-- SQL Server -->
WHERE username LIKE '%' + #{username} + '%'

性能考虑:

-- 前置通配符影响索引
WHERE username LIKE '%zhang%'  -- 不能使用索引-- 后置通配符可以使用索引
WHERE username LIKE 'zhang%'   -- 可以使用索引

4.5 统计查询

<select id="count" resultType="int">SELECT COUNT(*) FROM user
</select><select id="countByUsername" resultType="int">SELECT COUNT(*)FROM userWHERE username LIKE CONCAT('%', #{username}, '%')
</select>

resultType vs resultMap:

  • resultType=“int”:直接返回基本类型
  • resultMap:返回复杂对象类型
  • 自动转换:MyBatis自动处理类型转换

5. 参数传递机制

5.1 单参数传递

User selectById(Long id);
<select id="selectById" parameterType="Long" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userWHERE id = #{id}
</select>

单参数特点:

  • 自动识别:MyBatis自动识别参数类型
  • 直接引用:XML中直接使用#{参数名}
  • parameterType可选:通常可以省略

5.2 多参数传递

List<User> selectByPage(@Param("offset") Integer offset,@Param("limit") Integer limit);
<select id="selectByPage" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM userORDER BY id DESCLIMIT #{offset}, #{limit}
</select>

@Param注解必要性:

// 不使用@Param(不推荐)
List<User> selectByPage(Integer offset, Integer limit);
// XML中需要使用:#{param1}, #{param2} 或 #{0}, #{1}// 使用@Param(推荐)
List<User> selectByPage(@Param("offset") Integer offset,@Param("limit") Integer limit);
// XML中可以使用:#{offset}, #{limit}

5.3 对象参数传递

int insert(User user);
<insert id="insert" parameterType="com.example.usermanagement.entity.User">INSERT INTO user (username, password, email, phone, status, score)VALUES (#{username}, #{password}, #{email}, #{phone}, #{status}, #{score})
</insert>

对象属性访问:

  • 直接访问#{username} 等价于 user.getUsername()
  • 嵌套对象#{address.city} 访问嵌套属性
  • 类型安全:编译时检查属性是否存在

6. MyBatis配置优化

6.1 application.yml中的MyBatis配置

mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.example.usermanagement.entityconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl

配置说明:

  • mapper-locations:XML文件位置
  • type-aliases-package:实体类包路径
  • map-underscore-to-camel-case:自动驼峰转换
  • log-impl:SQL执行日志

6.2 自动驼峰转换效果

// 数据库字段 → Java属性
create_time → createTime
update_time → updateTime
user_name → userName

开启前后对比:

<!-- 未开启驼峰转换 -->
<resultMap id="UserResultMap" type="User"><result column="create_time" property="createTime"/><result column="update_time" property="updateTime"/>
</resultMap><!-- 开启驼峰转换后 -->
<!-- 可以省略ResultMap,MyBatis自动映射 -->
<select id="selectById" resultType="User">SELECT * FROM user WHERE id = #{id}
</select>

7. 性能优化要点

7.1 索引使用建议

-- 为常用查询字段建索引
CREATE INDEX idx_username ON user(username);
CREATE INDEX idx_email ON user(email);
CREATE INDEX idx_status ON user(status);-- 复合索引
CREATE INDEX idx_status_create_time ON user(status, create_time);

7.2 分页性能优化

<!-- 大数据量分页优化 -->
<select id="selectByPageOptimized" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM user WHERE id > #{lastId}ORDER BY idLIMIT #{limit}
</select>

游标分页 vs 传统分页:

-- 传统分页(深分页性能差)
SELECT * FROM user ORDER BY id LIMIT 100000, 10;-- 游标分页(性能稳定)
SELECT * FROM user WHERE id > 100000 ORDER BY id LIMIT 10;

7.3 批量操作支持

<!-- 批量插入 -->
<insert id="batchInsert" parameterType="list">INSERT INTO user (username, password, email)VALUES<foreach collection="list" item="user" separator=",">(#{user.username}, #{user.password}, #{user.email})</foreach>
</insert><!-- 批量删除 -->
<delete id="batchDelete" parameterType="list">DELETE FROM user WHERE id IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</delete>

8. 常见问题与解决方案

8.1 SQL注入防护

<!-- 安全的参数绑定 -->
WHERE username = #{username}    <!-- 推荐:预编译SQL --><!-- 危险的字符串拼接 -->
WHERE username = '${username}'  <!-- 不推荐:SQL注入风险 -->

#{}与${}区别:

  • #{}:预编译参数,防SQL注入
  • ${}:字符串替换,有注入风险

8.2 空值处理

<update id="update">UPDATE user<set><if test="username != null and username != ''">username = #{username},</if><if test="email != null and email != ''">email = #{email},</if></set>WHERE id = #{id}
</update>

8.3 数据库兼容性

<!-- MySQL -->
<select id="selectByPage" resultMap="BaseResultMap">SELECT * FROM user LIMIT #{offset}, #{limit}
</select><!-- Oracle -->
<select id="selectByPage" resultMap="BaseResultMap">SELECT * FROM (SELECT ROWNUM rn, t.* FROM user t WHERE ROWNUM <= #{offset} + #{limit}) WHERE rn > #{offset}
</select>

9. 高级特性应用

9.1 动态SQL复杂示例

<select id="searchUsers" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM user<where><if test="username != null and username != ''">AND username LIKE CONCAT('%', #{username}, '%')</if><if test="email != null and email != ''">AND email = #{email}</if><if test="status != null">AND status = #{status}</if><if test="minScore != null">AND score >= #{minScore}</if><if test="maxScore != null">AND score <= #{maxScore}</if></where>ORDER BY <choose><when test="sortBy == 'score'">score DESC</when><when test="sortBy == 'createTime'">create_time DESC</when><otherwise>id DESC</otherwise></choose>
</select>

9.2 结果集嵌套

<!-- 一对多关联查询 -->
<resultMap id="UserWithOrdersMap" type="User"><id column="id" property="id"/><result column="username" property="username"/><collection property="orders" ofType="Order"><id column="order_id" property="id"/><result column="order_amount" property="amount"/></collection>
</resultMap>
  1. 接口简洁:只需定义方法签名
  2. SQL分离:业务逻辑与SQL解耦
  3. 类型安全:编译时检查
  4. 动态SQL:灵活的条件查询
  5. 结果映射:自动对象转换
  6. 参数绑定:防SQL注入
  • 预编译SQL:性能优化

  • 结果缓存:二级缓存支持

  • 批量操作:减少数据库交互

  • 分页查询:大数据量处理

  • XML配置:SQL变更无需重编译

  • 代码复用:SQL片段共享

  • 规范统一:标准化的CRUD操作

  • 易于测试:接口便于Mock测试

http://www.xdnf.cn/news/17787.html

相关文章:

  • 全面解析MySQL(5)——“索引、事务、JDBC”三大核心
  • PostgreSQL——数据查询
  • 【K8s】部署安装K8s为什么要关闭swap分区?
  • Day50--图论--98. 所有可达路径(卡码网),797. 所有可能的路径
  • 元宇宙虚拟金融服务全景解析:技术创新、场景重构与未来趋势
  • 一体化步进伺服电机在无人机舱门应用中的应用案例
  • 使用Gradle手搓一个Kotlin/Native项目
  • CMU-15445(9)——PROJECT#3-Query Execution-Task#2Task#3
  • 机器学习-决策树(上)
  • TDengine 可观测性最佳实践
  • Nginx反向代理功能
  • 微前端架构:原理、场景与实践案例
  • 扫雷 (minesweeper)
  • 从0-1搭建webpack的前端工程化项目
  • 【前端基础】15、列表元素、表格元素、表单元素(注:极其粗略的记载。)
  • (3万字详解)Linux系统学习:深入了解Linux系统开发工具
  • js异步操作 Promise :fetch API 带来的网络请求变革—仙盟创梦IDE
  • Java Web项目后台管理系统之内容管理仿写:内容、搜索、页码加载
  • Zabbix携手Grafana打造炫酷监控大屏
  • 【Linux文件操作】文件操作系统调用
  • 19.Linux DHCP服务
  • 2025.8.6 图论(1)Solution
  • MySQL 基本语法
  • 对自己的 app 进行分析, 诊断,审视
  • 多路转接 select
  • 常见鱼饵制作方式
  • FPGA学习笔记——DS18B20(数字温度传感器)
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘lightgbm’问题
  • 【C++】封装哈希表模拟实现unordered_set和unordered_map
  • 简单的身份验证中间件Tinyauth