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

Mybatis入门、操作数据、配置xml映射、数据封装

Mybatis入门、操作数据、配置xml映射、数据封装

    • 1. MyBatis介绍、作用、相比JDBC的优势
      • 1.1 什么是MyBatis?
      • 1.2 MyBatis的核心作用
      • 1.3 相比JDBC的优势(用"做饭"类比)
    • 2. MyBatis入门实战
      • 2.1 准备工作(以"图书馆借阅系统"为例)
        • 2.1.1 数据库连接配置(application.properties)
        • 2.1.2 创建实体类(Book.java)
      • 2.2 创建Mapper接口(BookMapper.java)
      • 2.3 实现查询方法(两种方式)
        • 方式一:注解方式(简单SQL)
        • 方式二:XML配置方式(复杂SQL)
      • 2.4 IDEA配置SQL识别(避免红色警告)
      • 2.5 日志输出配置
    • 3. 数据库连接池详解
      • 3.1 什么是连接池?(用"奶茶店"类比)
      • 3.2 主流连接池对比
      • 3.3 切换为Druid连接池(以"换用更智能的奶茶机"为例)
        • 3.3.1 添加依赖(pom.xml)
        • 3.3.2 修改配置(application.properties)
    • 4. 删除数据操作
      • 4.1 基础删除案例(删除过期优惠券)
      • 4.2 #{}与${}的区别(用"快递签收"类比)
    • 4.3 动态条件删除(用"清理衣柜"举例)
    • 5. 新增和更新数据操作
      • 5.1 新增数据(以"添加新会员"为例)
        • 5.1.1 封装对象入参
        • 5.1.2 获取自增ID(像给新会员自动分配卡号)
      • 5.2 更新数据(以"修改收货地址"为例)
        • 5.2.1 全字段更新(替换整个地址)
        • 5.2.2 选择性更新(只改部分字段)
    • 6. 查询数据操作与@Param注解
      • 6.1 传递多个参数的三种方式
        • 方式一:使用@Param注解(推荐)
        • 方式二:使用Map传递参数(适合参数多的场景)
        • 方式三:封装对象(适合参数有逻辑关联的场景)
      • 6.2 @Param注解的强制使用场景
        • 场景一:非Spring Boot官方骨架(如阿里云Spring Boot)
        • 场景二:纯MyBatis项目(未集成Spring)
    • 7. MyBatis-XML映射配置详解
      • 7.1 XML映射三大规范(用"钥匙开锁"类比)
      • 7.2 XML映射的使用场景
      • 7.3 自定义XML文件存放路径(解决"文件乱放找不到"问题)
        • 问题场景
        • 解决方法:配置mapper-locations
      • 7.4 高级案例:多表关联查询(以"查询订单及商品信息"为例)
        • 7.4.1 定义结果映射(ResultMap)
        • 7.4.2 编写关联查询SQL
      • 7.5 常见问题解决:XML文件找不到的排查步骤
    • 8. 数据自动封装与类型不一致解决方案
      • 8.1 自动封装的默认规则("钥匙开锁"原理)
      • 8.2 三种解决方案(用"快递分拣"类比)
        • 8.2.1 方式一:@Results手动映射("人工分拣")
        • 8.2.2 方式二:SQL别名映射("贴标签分拣")
        • 8.2.3 方式三:开启驼峰命名("自动翻译机")
      • 8.3 三种方式对比与选择建议
      • 8.4 实战案例:混合使用三种方式

1. MyBatis介绍、作用、相比JDBC的优势

1.1 什么是MyBatis?

MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。可以将Java对象中的数据自动映射到数据库表中,也能将查询结果转换为Java对象,就像快递柜系统——帮你管理数据的"存取",无需手动处理复杂的数据库操作。

1.2 MyBatis的核心作用

  • 简化数据库操作:不用手动编写JDBC代码(加载驱动、创建连接、处理结果集等)
  • SQL与代码分离:SQL语句写在配置文件中,便于维护和优化
  • 自动映射:Java对象与数据库表字段自动对应,减少重复代码

1.3 相比JDBC的优势(用"做饭"类比)

场景JDBC方式(自己做饭)MyBatis方式(点外卖)
代码量需要写10行代码(买菜、洗菜、烹饪)只需1行代码(下单)
维护性修改SQL需改Java代码(改菜谱需重学)SQL单独存放(换菜品直接备注)
安全性需手动处理SQL注入(自己防骗)内置参数预编译(平台自动安检)
性能优化需手动实现连接池(自己买车库)内置连接池管理(外卖小哥调度系统)

2. MyBatis入门实战

2.1 准备工作(以"图书馆借阅系统"为例)

需求:查询读者借阅的图书信息
技术栈:Spring Boot + MyBatis + MySQL

2.1.1 数据库连接配置(application.properties)
# 数据库连接信息(像图书馆的地址和门禁密码)
spring.datasource.url=jdbc:mysql://localhost:3306/library?useSSL=false&serverTimezone=UTC
spring.datasource.username=root       # 数据库用户名(图书管理员账号)
spring.datasource.password=123456     # 数据库密码(管理员密码)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# MyBatis配置
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl  # 日志输出(监控借阅记录)
2.1.2 创建实体类(Book.java)
public class Book {private Integer id;         // 图书ID(像图书的条形码)private String name;        // 书名("Java编程思想")private String author;      // 作者("Bruce Eckel")private Integer borrowDays; // 借阅天数(7天)// Getter和Setter方法(相当于图书信息的"查看"和"修改"功能)public Integer getId() { return id; }public void setId(Integer id) { this.id = id; }// 省略其他getter/setter...
}

2.2 创建Mapper接口(BookMapper.java)

@Mapper  // 告诉MyBatis这是数据访问接口(图书管理员工作台)
public interface BookMapper {// 查询指定读者的借阅图书(根据读者ID查图书)List<Book> getBooksByReaderId(@Param("readerId") Integer readerId);
}

2.3 实现查询方法(两种方式)

方式一:注解方式(简单SQL)
@Select("SELECT * FROM book WHERE reader_id = #{readerId}")  // SQL语句(查询命令)
List<Book> getBooksByReaderId(@Param("readerId") Integer readerId);
方式二:XML配置方式(复杂SQL)

创建BookMapper.xml文件:

<?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"><!-- namespace必须和Mapper接口全类名一致 -->
<mapper namespace="com.example.library.mapper.BookMapper"><!-- id必须和接口方法名一致 --><select id="getBooksByReaderId" resultType="com.example.library.entity.Book">SELECT id, name, author, borrow_days FROM book WHERE reader_id = #{readerId}  <!-- #{readerId}是安全的参数占位符 --></select>
</mapper>

2.4 IDEA配置SQL识别(避免红色警告)

  1. 打开IDEA设置 File > Settings > Plugins
  2. 安装MyBatisX插件(像给IDE装"图书分类识别系统")
  3. 右键Mapper接口 > MyBatis > Generate Mapper XML自动生成XML文件

2.5 日志输出配置

application.properties中添加:

# 打印SQL执行日志(相当于图书馆的监控录像)
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

效果:控制台会显示执行的SQL语句和参数,例如:

==>  Preparing: SELECT * FROM book WHERE reader_id = ? 
==> Parameters: 1001(Integer)
<==    Columns: id, name, author, borrow_days
<==        Row: 1, Java编程思想, Bruce Eckel, 7

3. 数据库连接池详解

3.1 什么是连接池?(用"奶茶店"类比)

  • 传统方式:来一个顾客开一个新锅煮奶茶(每次请求创建新数据库连接)→ 效率低
  • 连接池方式:提前煮好10锅奶茶(创建10个连接)→ 顾客直接取用,喝完放回(连接复用)

3.2 主流连接池对比

连接池特点(用汽车类比)适用场景
HikariCP速度快(赛车),Spring Boot默认高并发系统(外卖高峰期)
Druid功能全(SUV),有监控和防SQL注入企业级应用(物流调度中心)

3.3 切换为Druid连接池(以"换用更智能的奶茶机"为例)

3.3.1 添加依赖(pom.xml)
<!-- Druid连接池依赖(相当于买新奶茶机) -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version>
</dependency>
3.3.2 修改配置(application.properties)
# 切换为Druid连接池(指定使用新奶茶机)
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource# Druid特有配置(奶茶机参数)
spring.datasource.druid.initial-size=5       # 初始连接数(开机预热5锅)
spring.datasource.druid.max-active=20        # 最大连接数(最多同时煮20锅)
spring.datasource.druid.min-idle=5           # 最小空闲连接(保底5锅)
spring.datasource.druid.test-on-borrow=true  # 借连接时检测可用性(出杯前尝一口)

4. 删除数据操作

4.1 基础删除案例(删除过期优惠券)

需求:删除超过30天未使用的优惠券
Mapper接口

public interface CouponMapper {// 无返回值方式(像扔垃圾,不需要知道扔了多少)void deleteExpiredCoupons(@Param("days") Integer days);// 有返回值方式(像清点垃圾,返回删除数量)int deleteExpiredCouponsWithCount(@Param("days") Integer days);
}

XML映射

<!-- 无返回值删除 -->
<delete id="deleteExpiredCoupons">DELETE FROM coupon WHERE create_time < DATE_SUB(NOW(), INTERVAL #{days} DAY)
</delete><!-- 有返回值删除(返回影响行数) -->
<delete id="deleteExpiredCouponsWithCount">DELETE FROM coupon WHERE create_time < DATE_SUB(NOW(), INTERVAL #{days} DAY)
</delete>

4.2 #{}与${}的区别(用"快递签收"类比)

占位符原理(签收方式)安全性使用场景举例
#{}预编译处理(快递柜代收,验货后签收)安全(防SQL注入)传递参数(用户ID、订单号)
${}字符串拼接(直接交给收件人,不验货)不安全(可能收到炸弹)动态表名(SELECT * FROM ${tableName}

危险示例:使用${}接收用户输入

// 用户输入:1; DROP TABLE user; (恶意代码)
String sql = "SELECT * FROM user WHERE id = ${id}"; 
// 拼接后变成:SELECT * FROM user WHERE id = 1; DROP TABLE user; (删库!)

4.3 动态条件删除(用"清理衣柜"举例)

需求:根据季节和颜色删除旧衣服

<delete id="deleteClothes">DELETE FROM clothes WHERE 1=1  <!-- 恒真条件,方便拼接AND --><if test="season != null">AND season = #{season}</if>  <!-- 如:season = 'summer' --><if test="color != null">AND color = #{color}</if>    <!-- 如:color = 'red' -->
</delete>

调用方式:删除所有红色夏装

clothesMapper.deleteClothes("summer", "red");

5. 新增和更新数据操作

5.1 新增数据(以"添加新会员"为例)

5.1.1 封装对象入参

实体类

public class Member {private String name;    // 姓名("张三")private Integer age;    // 年龄(25)private String phone;   // 电话("13800138000")// Getter/Setter省略
}

Mapper接口

// 添加会员(参数是Member对象)
int addMember(Member member);

XML配置

<insert id="addMember">INSERT INTO member(name, age, phone) VALUES(#{name}, #{age}, #{phone})  <!-- 直接使用对象属性名 -->
</insert>

调用方式

Member newMember = new Member();
newMember.setName("张三");
newMember.setAge(25);
newMember.setPhone("13800138000");
memberMapper.addMember(newMember);  // 像把填好的会员表交给前台
5.1.2 获取自增ID(像给新会员自动分配卡号)
<insert id="addMember" useGeneratedKeys="true" keyProperty="id"><!-- useGeneratedKeys:使用自增主键 --><!-- keyProperty:将生成的ID赋值给对象的id属性 -->INSERT INTO member(name, age) VALUES(#{name}, #{age})
</insert>

调用后获取ID

memberMapper.addMember(newMember);
System.out.println(newMember.getId());  // 输出:10086(自动生成的ID)

5.2 更新数据(以"修改收货地址"为例)

5.2.1 全字段更新(替换整个地址)
<update id="updateAddress">UPDATE user SET province=#{province}, city=#{city}, street=#{street}WHERE id=#{userId}
</update>
5.2.2 选择性更新(只改部分字段)
<update id="updateAddressSelective">UPDATE user <set>  <!-- set标签自动处理逗号 --><if test="province != null">province=#{province},</if><if test="city != null">city=#{city},</if><if test="street != null">street=#{street}</if></set>WHERE id=#{userId}
</update>

优势:只更新不为null的字段(改城市时不影响省份和街道)

6. 查询数据操作与@Param注解

6.1 传递多个参数的三种方式

方式一:使用@Param注解(推荐)
// 查询"张三"在"2023年"的订单(两个独立参数)
List<Order> getOrders(@Param("username") String username,  // 显式命名参数@Param("year") String year
);
<select id="getOrders" resultType="Order">SELECT * FROM order WHERE username = #{username} AND create_time LIKE CONCAT(#{year}, '%')  <!-- 如:2023% -->
</select>
方式二:使用Map传递参数(适合参数多的场景)
Map<String, Object> params = new HashMap<>();
params.put("username", "张三");
params.put("year", "2023");
List<Order> orders = orderMapper.getOrdersByMap(params);
方式三:封装对象(适合参数有逻辑关联的场景)
public class OrderQuery {private String username;private String year;// Getter/Setter
}
List<Order> getOrdersByObject(OrderQuery query);

6.2 @Param注解的强制使用场景

场景一:非Spring Boot官方骨架(如阿里云Spring Boot)
  • 原因:这些骨架可能没有配置MyBatis的参数自动命名功能,导致多参数无法识别
  • 解决:必须加@Param注解明确参数名
场景二:纯MyBatis项目(未集成Spring)
  • 原因:MyBatis原生不支持参数名自动映射,需要通过@Param指定
  • 示例
// 纯MyBatis环境必须加@Param
List<User> findUsers(@Param("name") String name, @Param("age") Integer age);

7. MyBatis-XML映射配置详解

7.1 XML映射三大规范(用"钥匙开锁"类比)

  1. 同包同名:Mapper接口和XML文件放在同一个包,文件名相同
    → 例:com.example.mapper.UserMapper.javacom.example.mapper.UserMapper.xml

  2. namespace一致:XML的namespace必须等于Mapper接口的全类名

    <!-- 正确 -->
    <mapper namespace="com.example.mapper.UserMapper">
    <!-- 错误:namespace与接口名不符 -->
    <mapper namespace="com.example.dao.UserDao">
    
  3. SQL id与方法名一致:XML中SQL语句的id必须等于接口中的方法名

    <!-- 正确:对应UserMapper接口的getUserById方法 -->
    <select id="getUserById" resultType="User">
    <!-- 错误:id与方法名不符 -->
    <select id="selectUser" resultType="User">
    

7.2 XML映射的使用场景

场景适合方式原因分析
简单SQL(单表CRUD)注解方式代码集中,直观简洁
复杂SQL(多表关联、动态SQL)XML方式SQL与Java代码分离,便于维护
需要团队协作编写SQLXML方式DBA可直接修改XML文件

7.3 自定义XML文件存放路径(解决"文件乱放找不到"问题)

问题场景

如果不按规范放(如XML文件想放resources/mapper目录下),MyBatis会找不到XML文件

解决方法:配置mapper-locations

在application.properties中添加:

# 指定XML文件存放路径(classpath表示resources目录)
mybatis.mapper-locations=classpath:mapper/*.xml

目录结构

src/main/resources/
└── mapper/├── UserMapper.xml└── OrderMapper.xml

7.4 高级案例:多表关联查询(以"查询订单及商品信息"为例)

7.4.1 定义结果映射(ResultMap)
<resultMap id="OrderAndProductMap" type="OrderVO"><!-- 订单表字段 --><id property="orderId" column="order_id"/><result property="orderTime" column="order_time"/><!-- 关联商品表(一对一) --><association property="product" javaType="Product"><id property="productId" column="product_id"/><result property="productName" column="product_name"/></association>
</resultMap>
7.4.2 编写关联查询SQL
<select id="getOrderWithProduct" resultMap="OrderAndProductMap">SELECT o.id as order_id, o.time as order_time,p.id as product_id, p.name as product_nameFROM `order` oLEFT JOIN product p ON o.product_id = p.idWHERE o.id = #{orderId}
</select>

7.5 常见问题解决:XML文件找不到的排查步骤

  1. 检查文件名:确保XML文件名与接口名完全一致(含大小写)
  2. 检查路径:确认XML文件在resources目录下,且与配置的mapper-locations匹配
  3. Maven打包检查:在pom.xml中添加资源文件打包配置(防止XML被过滤)
<build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.xml</include>  <!-- 确保XML被打包 --></includes></resource></resources>
</build>

8. 数据自动封装与类型不一致解决方案

8.1 自动封装的默认规则("钥匙开锁"原理)

MyBatis的数据封装就像钥匙匹配锁芯——数据库列名必须与实体类属性名完全一致才能自动映射。
示例

  • 数据库表字段:user_name(下划线命名)
  • 实体类属性:userName(驼峰命名)
    默认不匹配:MyBatis找不到对应的属性,封装结果为null

8.2 三种解决方案(用"快递分拣"类比)

8.2.1 方式一:@Results手动映射(“人工分拣”)

适用场景:字段名与属性名差异大(如u_iduserId
实现步骤

  1. 在Mapper接口方法上添加@Results注解
  2. @Result指定列名与属性名的对应关系

代码示例(查询用户信息):

@Select("SELECT u_id, u_name, u_age FROM t_user WHERE u_id = #{id}")
@Results({@Result(column = "u_id", property = "userId"),    // 列u_id → 属性userId@Result(column = "u_name", property = "userName"),// 列u_name → 属性userName@Result(column = "u_age", property = "userAge")   // 列u_age → 属性userAge
})
User getUserById(Integer id);

优点:灵活性高,可自定义映射规则
缺点:代码冗余,每个方法需单独配置

8.2.2 方式二:SQL别名映射(“贴标签分拣”)

适用场景:少量字段不一致,临时调整
实现步骤:在SQL中用AS关键字为列名起别名,使其与属性名一致

代码示例

<select id="getUser" resultType="User">SELECT u_id AS userId,    <!-- 别名userId匹配实体类属性 -->u_name AS userName,u_age AS userAgeFROM t_user WHERE u_id = #{id}
</select>

生活类比:给快递包裹贴上新标签(别名),确保快递员(MyBatis)能正确识别目的地

8.2.3 方式三:开启驼峰命名(“自动翻译机”)

适用场景:数据库列名用下划线(如user_name),实体类用驼峰命名(userName)的统一规则场景
实现步骤:在application.properties中添加全局配置

# 开启下划线转驼峰命名(如user_name → userName)
mybatis.configuration.map-underscore-to-camel-case=true

效果:自动映射以下场景

  • 数据库列:order_id → 实体类属性:orderId
  • 数据库列:create_time → 实体类属性:createTime

注意:需保证列名和属性名的核心部分一致(如order_idorderId的"order"部分匹配)

8.3 三种方式对比与选择建议

解决方式配置复杂度适用场景维护成本
手动映射字段名与属性名差异大(如u_id→userId)高(每个方法单独维护)
SQL别名少量字段不一致,临时调整中(SQL中维护别名)
驼峰命名极低统一使用下划线/驼峰命名规则低(全局配置)

选择建议

  • 新项目:优先使用驼峰命名(一劳永逸)
  • 老项目:少量字段用SQL别名,大量不匹配用手动映射
  • 特殊场景(如列名含缩写):必须用手动映射

8.4 实战案例:混合使用三种方式

需求:查询订单信息,包含以下字段映射

  • o_idorderId(手动映射)
  • order_snorderSn(驼峰命名,需开启配置)
  • total_amttotalAmount(SQL别名)

实现步骤

  1. 开启驼峰命名配置
  2. Mapper接口方法:
@Select("SELECT o_id, order_sn, total_amt AS totalAmount FROM t_order WHERE o_id = #{id}")
@Results({@Result(column = "o_id", property = "orderId")  // 手动映射特殊字段
})
Order getOrderById(Integer id);

解析

  • o_idorderId:手动映射(因"o_"前缀无法通过驼峰转换)
  • order_snorderSn:驼峰命名自动转换(o_sn→orderSn)
  • total_amt AS totalAmount:SQL别名映射

通过这种混合方式,灵活处理各种映射场景。

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

相关文章:

  • 深入探讨AI三大领域的核心技术、实践方法以及未来发展趋势,结合具体代码示例、流程图和Prompt工程实践,全面展示AI编程的强大能力。
  • leetcode21.合并两个有序链表
  • 来自AI的背包系统
  • solar应急响应-7月
  • 怎样让外网计算机访问局域网计算机?通过公网地址访问不同内网服务的设置方法
  • Web 与 Nginx 网站服务介绍与nginx安装
  • 泛型-泛型方法
  • C++工程实战入门笔记10-面向对象之静态成员变量和成员函数、构造函数和析构函数
  • 【C++设计模式】第二篇:策略模式(Strategy)--从基本介绍,内部原理、应用场景、使用方法,常见问题和解决方案进行深度解析
  • 联软科技:以“韧性安全”守护数字世界,致敬抗战胜利80周年的坚韧精神
  • vite与webpack对比
  • ATT层MTU大小
  • 【工具变量】数林指数数据集(2017-2024年)
  • 力扣654:最大二叉树
  • 51单片机-按键、蜂鸣器、定时器模块及中断
  • 大文件断点续传解决方案:基于Vue 2与Spring Boot的完整实现
  • C++并发编程-23. 线程间切分任务的方法
  • `void 0` 与 `undefined` 深度解析
  • mysql安装(压缩包方式8.0及以上)
  • 2026届IC秋招联芸科技IC面经(完整面试题)
  • 从零开始学大模型之大语言模型
  • 大模型部署全攻略:Docker+FastAPI+Nginx搭建高可用AI服务
  • MindMeister AI版:AI思维导图工具高效生成框架,解决结构卡壳与逻辑优化难题
  • 十一、容器化 vs 虚拟化-K8s-Kustomize
  • Spark中的堆外和堆内内存以及内部行数据表示UnsafeRow
  • S 3.3深度学习--卷积神经网络--代码
  • (A题|烟幕干扰弹的投放策略)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • 【mmcv自己理解】
  • “全结构化录入+牙位可视化标记”人工智能化python编程路径探析
  • 新电脑硬盘如何分区?3个必知技巧避免“空间浪费症”!