JavaWeb快速入门: MyBatis入门与实战

本文纲要

  1. MyBatis简介
  2. 快速入门
  3. Mapper代理开发
  4. MyBatis核心配置文件详解
  5. 增删改查实战
    5.1 环境准备
    5.2 查询所有 & 结果映射
    5.3 查看详情 & 参数占位符
    5.4 条件查询 & 多参数接收
    5.5 动态SQL
    5.6 添加 & 主键返回
    5.7 修改全部/动态字段
    5.8 删除一个/批量删除
  6. MyBatis参数传递原理
  7. 注解开发
  8. 总结

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 ) 代理开发规则

  1. 定义与SQL映射文件同名的Mapper接口,并放置在同一目录下。
  2. 设置SQL映射文件的 namespace 为接口的全限定名。
  3. 接口方法名与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_namecompany_name(下划线),实体类属性为 brandNamecompanyName(驼峰),导致封装失败。

解决方案

方案一:起别名

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中包含 <> 等特殊字符时,可使用:

  1. 转义字符:&lt; 表示 <。
  2. <![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为 collectionarg0
List 封装为Map,key为 collectionlistarg0
Array 封装为Map,key为 arrayarg0
其他类型(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等增强工具,进一步提升效率。

相关推荐
EntyIU1 小时前
Java NIO 实战
java·开发语言·nio
二宝哥1 小时前
springboot项目使用Gradle工具实现依赖版本控制
java·spring boot·后端·gradle·版本
济*沧*海2 小时前
Set集合深度解析:原理、特性与应用场景全攻略
java
geovindu2 小时前
java: Singleton Pattern
java·开发语言·后端·单例模式·设计模式·创建型模式
一路向北North2 小时前
Spring Security OAuth2.0(16):密码模式
java·后端·spring
水无痕simon2 小时前
5 多表操作
java·开发语言·数据库
ALex_zry2 小时前
C++26 Freestanding 库扩展详解:无操作系统也能用标准库
java·jvm·c++
Tian_Hang2 小时前
Eclipse Mosquitto 安装及介绍
java·运维·服务器·ide·sql·mysql·eclipse
weelinking2 小时前
weytoken微元算力企业级大模型治理平台深度评测
java·大数据·人工智能