本文纲要
MyBatis简介- 快速入门
Mapper代理开发MyBatis核心配置文件详解- 增删改查实战
5.1 环境准备
5.2 查询所有 & 结果映射
5.3 查看详情 & 参数占位符
5.4 条件查询 & 多参数接收
5.5 动态SQL
5.6 添加 & 主键返回
5.7 修改全部/动态字段
5.8 删除一个/批量删除 MyBatis参数传递原理- 注解开发
- 总结
MyBatis简介
MyBatis 是一款优秀的持久层框架,用于简化JDBC的开发。
在Java EE中经典的三层架构为:
表现层:页面展示业务层:逻辑处理持久层:负责将数据保存到数据库
MyBatis 就是专注于持久层的框架。
JDBC的缺点
传统的JDBC代码存在两个突出问题:
- 硬编码:数据库连接信息、SQL语句直接写在Java代码中,变更需修改源码并重新编译部署。
- 操作繁琐:手动设置参数、手动封装结果集。
MyBatis如何简化
MyBatis通过以下方式解决JDBC的痛点:
- 将数据库连接信息抽取到 核心配置文件 中。
- 将SQL语句抽取到 SQL映射文件 中。
- 自动完成参数设置和结果集封装,一行代码即可执行查询并返回对象集合。
下图对比了传统JDBC与MyBatis的开发方式:
#mermaid-svg-uAcD6SmGnMNlbYyX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uAcD6SmGnMNlbYyX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uAcD6SmGnMNlbYyX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uAcD6SmGnMNlbYyX .error-icon{fill:#552222;}#mermaid-svg-uAcD6SmGnMNlbYyX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uAcD6SmGnMNlbYyX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uAcD6SmGnMNlbYyX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uAcD6SmGnMNlbYyX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uAcD6SmGnMNlbYyX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uAcD6SmGnMNlbYyX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uAcD6SmGnMNlbYyX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uAcD6SmGnMNlbYyX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uAcD6SmGnMNlbYyX .marker.cross{stroke:#333333;}#mermaid-svg-uAcD6SmGnMNlbYyX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uAcD6SmGnMNlbYyX p{margin:0;}#mermaid-svg-uAcD6SmGnMNlbYyX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uAcD6SmGnMNlbYyX .cluster-label text{fill:#333;}#mermaid-svg-uAcD6SmGnMNlbYyX .cluster-label span{color:#333;}#mermaid-svg-uAcD6SmGnMNlbYyX .cluster-label span p{background-color:transparent;}#mermaid-svg-uAcD6SmGnMNlbYyX .label text,#mermaid-svg-uAcD6SmGnMNlbYyX span{fill:#333;color:#333;}#mermaid-svg-uAcD6SmGnMNlbYyX .node rect,#mermaid-svg-uAcD6SmGnMNlbYyX .node circle,#mermaid-svg-uAcD6SmGnMNlbYyX .node ellipse,#mermaid-svg-uAcD6SmGnMNlbYyX .node polygon,#mermaid-svg-uAcD6SmGnMNlbYyX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uAcD6SmGnMNlbYyX .rough-node .label text,#mermaid-svg-uAcD6SmGnMNlbYyX .node .label text,#mermaid-svg-uAcD6SmGnMNlbYyX .image-shape .label,#mermaid-svg-uAcD6SmGnMNlbYyX .icon-shape .label{text-anchor:middle;}#mermaid-svg-uAcD6SmGnMNlbYyX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uAcD6SmGnMNlbYyX .rough-node .label,#mermaid-svg-uAcD6SmGnMNlbYyX .node .label,#mermaid-svg-uAcD6SmGnMNlbYyX .image-shape .label,#mermaid-svg-uAcD6SmGnMNlbYyX .icon-shape .label{text-align:center;}#mermaid-svg-uAcD6SmGnMNlbYyX .node.clickable{cursor:pointer;}#mermaid-svg-uAcD6SmGnMNlbYyX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uAcD6SmGnMNlbYyX .arrowheadPath{fill:#333333;}#mermaid-svg-uAcD6SmGnMNlbYyX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uAcD6SmGnMNlbYyX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uAcD6SmGnMNlbYyX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uAcD6SmGnMNlbYyX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uAcD6SmGnMNlbYyX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uAcD6SmGnMNlbYyX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uAcD6SmGnMNlbYyX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uAcD6SmGnMNlbYyX .cluster text{fill:#333;}#mermaid-svg-uAcD6SmGnMNlbYyX .cluster span{color:#333;}#mermaid-svg-uAcD6SmGnMNlbYyX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uAcD6SmGnMNlbYyX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uAcD6SmGnMNlbYyX rect.text{fill:none;stroke-width:0;}#mermaid-svg-uAcD6SmGnMNlbYyX .icon-shape,#mermaid-svg-uAcD6SmGnMNlbYyX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uAcD6SmGnMNlbYyX .icon-shape p,#mermaid-svg-uAcD6SmGnMNlbYyX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uAcD6SmGnMNlbYyX .icon-shape .label rect,#mermaid-svg-uAcD6SmGnMNlbYyX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uAcD6SmGnMNlbYyX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uAcD6SmGnMNlbYyX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uAcD6SmGnMNlbYyX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MyBatis
加载核心配置文件
获取SqlSessionFactory
获取SqlSession
获取Mapper代理
调用接口方法
自动返回封装好的对象
JDBC
注册驱动
获取连接
定义SQL
设置参数
执行SQL
遍历结果集封装对象
MyBatis 免除了几乎所有的JDBC代码以及参数设置和获取结果集的工作,极大地提升了开发效率和可维护性。
快速入门
1 ) 需求
查询 user 表中所有数据,封装为 User 对象并装入集合。
2 ) 环境搭建
2.1 项目结构
dir
mybatis-demo
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── wb
│ │ │ ├── MyBatisDemo.java
│ │ │ ├── mapper
│ │ │ │ └── UserMapper.java
│ │ │ └── pojo
│ │ │ └── User.java
│ │ └── resources
│ │ ├── mybatis-config.xml
│ │ ├── logback.xml
│ │ └── com
│ │ └── wb
│ │ └── mapper
│ │ └── UserMapper.xml
│ └── test
│ └── java
│ └── com
│ └── wb
│ └── test
│ └── MyBatisTest.java
2.2 数据库表
sql
CREATE DATABASE mybatis;
USE mybatis;
DROP TABLE IF EXISTS tb_user;
CREATE TABLE tb_user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20),
password VARCHAR(20),
gender CHAR(1),
addr VARCHAR(30)
);
INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京');
INSERT INTO tb_user VALUES (2, 'lisi', '456', '女', '上海');
INSERT INTO tb_user VALUES (3, 'wangwu', '789', '男', '广州');
2.3 Maven依赖 (pom.xml)
xml
<dependencies>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
3 ) 编写MyBatis核心配置文件
mybatis-config.xml
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/wb/mapper/UserMapper.xml"/>
</mappers>
</configuration>
4 ) 编写SQL映射文件
UserMapper.xml
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">
<mapper namespace="test">
<select id="selectAll" resultType="com.wb.pojo.User">
select * from tb_user;
</select>
</mapper>
5 ) 编写POJO类
java
public class User {
private Integer id;
private String username;
private String password;
private String gender;
private String addr;
// getter/setter/toString 省略
}
6 ) 编写测试代码
java
public class MyBatisDemo {
public static void main(String[] args) throws IOException {
// 1. 加载核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 执行SQL
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);
// 4. 释放资源
sqlSession.close();
}
}
7 ) 解决SQL映射文件警告
在IDEA中配置数据库连接后,SQL映射文件中的表名、字段名会得到智能提示,警告消失。步骤:
View -> Tool Windows -> Database -> + -> Data Source -> MySQL,填写连接信息并测试。
Mapper代理开发
快速入门中直接使用字符串调用SQL存在硬编码问题,且不方便。Mapper代理开发 是MyBatis的主流开发方式,它通过接口方法与SQL映射绑定,提供代码提示和类型安全。
1 ) 代理开发规则
- 定义与SQL映射文件同名的Mapper接口,并放置在同一目录下。
- 设置SQL映射文件的 namespace 为接口的全限定名。
- 接口方法名与SQL语句的 id 保持一致,参数和返回值类型匹配。
2 ) 代码实现
调整项目结构(在resources下用 / 创建与接口相同的包结构):
src/main/resources/com/wb/mapper/UserMapper.xml
src/main/java/com/wb/mapper/UserMapper.java
2.1 UserMapper接口
java
public interface UserMapper {
List<User> selectAll();
}
2.2 UserMapper.xml
xml
<mapper namespace="com.wb.mapper.UserMapper">
<select id="selectAll" resultType="user">
select * from tb_user;
</select>
</mapper>
注意:resultType 使用了别名 user,需在核心配置文件中配置别名。
核心配置文件 支持包扫描加载映射文件:
xml
<typeAliases>
<package name="com.wb.pojo"/>
</typeAliases>
<mappers>
<package name="com.wb.mapper"/>
</mappers>
2.1 测试代码
java
public class MyBatisDemo2 {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取Mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
System.out.println(users);
sqlSession.close();
}
}
3 ) 代理执行流程
UserMapper.xml UserMapper(代理) SqlSession Client UserMapper.xml UserMapper(代理) SqlSession Client #mermaid-svg-t6D36C0qRnxquSOp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t6D36C0qRnxquSOp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t6D36C0qRnxquSOp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t6D36C0qRnxquSOp .error-icon{fill:#552222;}#mermaid-svg-t6D36C0qRnxquSOp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t6D36C0qRnxquSOp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t6D36C0qRnxquSOp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t6D36C0qRnxquSOp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t6D36C0qRnxquSOp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t6D36C0qRnxquSOp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t6D36C0qRnxquSOp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t6D36C0qRnxquSOp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t6D36C0qRnxquSOp .marker.cross{stroke:#333333;}#mermaid-svg-t6D36C0qRnxquSOp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t6D36C0qRnxquSOp p{margin:0;}#mermaid-svg-t6D36C0qRnxquSOp .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-t6D36C0qRnxquSOp text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-t6D36C0qRnxquSOp .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-t6D36C0qRnxquSOp .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-t6D36C0qRnxquSOp .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-t6D36C0qRnxquSOp .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-t6D36C0qRnxquSOp #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-t6D36C0qRnxquSOp .sequenceNumber{fill:white;}#mermaid-svg-t6D36C0qRnxquSOp #sequencenumber{fill:#333;}#mermaid-svg-t6D36C0qRnxquSOp #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-t6D36C0qRnxquSOp .messageText{fill:#333;stroke:none;}#mermaid-svg-t6D36C0qRnxquSOp .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-t6D36C0qRnxquSOp .labelText,#mermaid-svg-t6D36C0qRnxquSOp .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-t6D36C0qRnxquSOp .loopText,#mermaid-svg-t6D36C0qRnxquSOp .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-t6D36C0qRnxquSOp .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-t6D36C0qRnxquSOp .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-t6D36C0qRnxquSOp .noteText,#mermaid-svg-t6D36C0qRnxquSOp .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-t6D36C0qRnxquSOp .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-t6D36C0qRnxquSOp .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-t6D36C0qRnxquSOp .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-t6D36C0qRnxquSOp .actorPopupMenu{position:absolute;}#mermaid-svg-t6D36C0qRnxquSOp .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-t6D36C0qRnxquSOp .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-t6D36C0qRnxquSOp .actor-man circle,#mermaid-svg-t6D36C0qRnxquSOp line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-t6D36C0qRnxquSOp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} getMapper(UserMapper.class)返回代理对象selectAll()根据方法名匹配id="selectAll"SQL与resultType执行selectList返回List<User>
MyBatis核心配置文件详解
mybatis-config.xml 的结构如下(必须按顺序):
configuration
├── properties
├── settings
├── typeAliases
├── typeHandlers
├── objectFactory
├── plugins
├── environments
│ └── environment
│ ├── transactionManager
│ └── dataSource
└── mappers
1 ) environments(环境配置)
可配置多个数据源环境,通过 default 属性切换:
xml
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
<environment id="test">
<!-- 测试环境配置 -->
</environment>
</environments>
2 ) typeAliases(类型别名)
通过包扫描为实体类自动起别名(类名不区分大小写):
xml
<typeAliases>
<package name="com.wb.pojo"/>
</typeAliases>
之后在映射文件中可直接写 resultType="user" 或 resultType="User"。
3 ) mappers(映射器)
直接加载XML:<mapper resource="com/wb/mapper/UserMapper.xml"/>
包扫描(需Mapper接口与XML同名同目录):
增删改查实战
以下以品牌表 tb_brand 为例,演示完整的CRUD及动态SQL。
1 ) 环境准备
数据库表
sql
DROP TABLE IF EXISTS tb_brand;
CREATE TABLE tb_brand (
id INT PRIMARY KEY AUTO_INCREMENT,
brand_name VARCHAR(50),
company_name VARCHAR(50),
ordered INT,
description VARCHAR(200),
status INT
);
INSERT INTO tb_brand VALUES (1, '三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0);
INSERT INTO tb_brand VALUES (2, '华为', '华为技术有限公司', 100, '华为,不仅仅是世界500强', 1);
INSERT INTO tb_brand VALUES (3, '小米', '小米科技有限公司', 50, 'are you ok', 1);
Brand实体类
java
public class Brand {
private Integer id;
private String brandName;
private String companyName;
private Integer ordered;
private String description;
private Integer status;
// getter/setter/toString 省略
}
BrandMapper接口
java
public interface BrandMapper {
List<Brand> selectAll();
Brand selectById(int id);
// 其他方法后续补充
}
BrandMapper.xml 初始配置:
xml
<mapper namespace="com.wb.mapper.BrandMapper">
<resultMap id="brandResultMap" type="brand">
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<!-- SQL语句待添加 -->
</mapper>
2 ) 查询所有 & 结果映射
问题:字段名与属性名不一致
数据库列名为 brand_name、company_name(下划线),实体类属性为 brandName、companyName(驼峰),导致封装失败。
解决方案
方案一:起别名
xml
<select id="selectAll" resultType="brand">
select id, brandname as brandName, company_name as companyName, ordered, description, status
from tb_brand;
</select>
缺点:每个查询都要重复定义别名。
方案二:使用 resultMap(推荐)
xml
<resultMap id="brandResultMap" type="brand">
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select * from tb_brand;
</select>
<resultMap>定义映射规则,id为主键映射,result为普通字段映射。column:数据库列名;property:实体类属性名。- 只定义不一致的字段,一致的会自动映射。
测试代码
java
@Test
public void testSelectAll() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);
sqlSession.close();
}
3 ) 查看详情 & 参数占位符
需求:根据ID查询品牌。
接口方法
java
Brand selectById(int id);
SQL映射
xml
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id = #{id};
</select>
参数占位符 #{} 与 ${}
#{}:会替换为?,相当于PreparedStatement,防止SQL注入。${}:直接拼接字符串,存在SQL注入风险,仅在表名或列名不固定时使用。
示例:where id = #{id} 生成 where id = ?;where id = ${id} 生成 where id = 1。
特殊字符处理
当SQL中包含 <、> 等特殊字符时,可使用:
- 转义字符:
<表示 <。 <![CDATA[ ... ]]>区段。
xml
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand
where id <![CDATA[ < ]]> #{id};
</select>
4 ) 条件查询 & 多参数接收
需求:根据状态、企业名称、品牌名称进行多条件查询。
接口方法
java
// 方式1:散装参数,使用@Param指定名称
List<Brand> selectByCondition(@Param("status") int status,
@Param("companyName") String companyName,
@Param("brandName") String brandName);
// 方式2:对象参数
List<Brand> selectByCondition(Brand brand);
// 方式3:Map参数
List<Brand> selectByCondition(Map map);
SQL映射
xml
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
测试代码(以散装参数为例)
java
@Test
public void testSelectByCondition() throws IOException {
int status = 1;
String companyName = "%华为%";
String brandName = "%华为%";
// ... 获取SqlSession和BrandMapper
List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);
System.out.println(brands);
// ...
}
三种接收方式对比:
| 方式 | 要求 | 适用场景 |
|---|---|---|
散装参数 + @Param |
@Param中的值与占位符名称一致 |
参数较少且独立 |
| 对象参数 | 对象属性名与占位符名称一致 | 参数属于同一实体 |
| Map参数 | Map的key与占位符名称一致 | 动态组装参数 |
5 ) 动态SQL
5.1 多条件动态查询
用户可能只输入部分条件,SQL需动态变化。使用 <if> 和 <where> 标签。
xml
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
<where>
<if test="status != null">
and status = #{status}
</if>
<if test="companyName != null and companyName != ''">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != ''">
and brand_name like #{brandName}
</if>
</where>
</select>
<where>自动处理第一个and,并动态生成where关键字。<if test="...">判断参数是否有效。
5.2 单条件动态查询
用户从多个条件中选择一个进行查询,使用 <choose>、<when>、<otherwise>。
xml
<select id="selectByConditionSingle" resultMap="brandResultMap">
select * from tb_brand
<where>
<choose>
<when test="status != null">
status = #{status}
</when>
<when test="companyName != null and companyName != ''">
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != ''">
brand_name like #{brandName}
</when>
<!-- otherwise 可省略,配合where标签自动处理无条件的场景 -->
</choose>
</where>
</select>
5.3 修改动态字段
修改时只更新传入的非空字段,使用 <set> 标签。
xml
<update id="update">
update tb_brand
<set>
<if test="brandName != null and brandName != ''">
brand_name = #{brandName},
</if>
<if test="companyName != null and companyName != ''">
company_name = #{companyName},
</if>
<if test="ordered != null">
ordered = #{ordered},
</if>
<if test="description != null and description != ''">
description = #{description},
</if>
<if test="status != null">
status = #{status}
</if>
</set>
where id = #{id};
</update>
<set>自动去除末尾多余的逗号。
5.4 批量删除
根据ID数组批量删除,使用 <foreach> 遍历。
xml
<delete id="deleteByIds">
delete from tb_brand where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
接口方法需使用 @Param("ids") 指定数组名称:
java
void deleteByIds(@Param("ids") int[] ids);
<foreach>属性:collection:要遍历的集合(数组默认可用array,或通过@Param指定)。item:每个元素。separator:分隔符。open/close:拼接开始/结束字符。
5.6 添加 & 主键返回
接口方法
java
void add(Brand brand);
SQL映射
xml
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tbbrand (brandname, company_name, ordered, description, status)
values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>
useGeneratedKeys="true":启用主键回写。keyProperty="id":将生成的主键赋值给实体类的id属性。
测试
java
brandMapper.add(brand);
sqlSession.commit(); // 默认需手动提交事务
System.out.println(brand.getId()); // 可获取到自增ID
5.7 修改全部/动态字段
修改全部字段
xml
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id = #{id};
</update>
动态字段修改(见5.3)
5.8 删除一个/批量删除
删除一个
java
void deleteById(int id);
xml
<delete id="deleteById">
delete from tb_brand where id = #{id};
</delete>
批量删除(见5.4)
MyBatis参数传递原理
MyBatis通过 ParamNameResolver 对接口方法参数进行封装。
1 ) 多个参数
多个参数会被封装为一个 Map,默认key为:
arg0,arg1... (顺序参数)param1,param2... (参数位置)
使用 @Param 注解可自定义key,替换 arg0 等默认值,推荐始终使用 @Param 以提高可读性。
2 ) 单个参数
| 参数类型 | 封装规则 |
|---|---|
| POJO | 直接使用,属性名需与占位符一致 |
| Map | 直接使用,key需与占位符一致 |
| Collection | 封装为Map,key为 collection 和 arg0 |
| List | 封装为Map,key为 collection、list、arg0 |
| Array | 封装为Map,key为 array 和 arg0 |
| 其他类型(int、String等) | 直接使用,占位符名称任意 |
最佳实践 :无论单参数还是多参数,都使用 @Param 明确指定名称,使SQL映射清晰稳定。
注解开发
MyBatis也支持通过注解直接在接口方法上编写SQL,适合简单CRUD。
1 ) 常用注解
@Select@Insert@Update@Delete
示例
java
public interface UserMapper {
@Select("select * from tb_user where id = #{id}")
User selectById(int id);
}
2 ) 注解 vs XML
注解:简单语句更简洁,但复杂SQL(如动态SQL)会使代码混乱。XML:适合复杂语句,动态SQL标签更清晰。
官方建议:简单语句用注解,复杂语句用XML。
总结
本文从MyBatis的基本概念出发,通过快速入门、Mapper代理开发、核心配置、增删改查实战、动态SQL、参数原理及注解开发,系统讲解了MyBatis的核心知识。
掌握了这些内容,你已经能够应对日常开发中大部分持久层需求。后续可继续学习MyBatis-Plus等增强工具,进一步提升效率。