【JavaWeb学习记录】MyBatis核心知识点与实战笔记:从入门到动态SQL

Javaweb学习记录 --- MyBatis

日期: 11.8

📖今日知识点 ------Mybatis简介

  • 什么是Mybatis?

    复制代码
      1.Mybatis是一款优秀的持久层框架,用于简化JDBC开发
      2.Mybatis本是Apache的一个开源项目iBatis,2021年这个项目有apache software foundation迁移到了Google code,并且改名为Mybatis。2013年11月迁移到GitHub。
      3.[官网](https://mybatis.org/mybatis-3/zh/index.html)
  • 什么是持久层?

    1.负责将数据保存到数据库的那一层代码

    2.JavaEE三层架构:表现层、业务层、持久层

  • 什么是框架?

    复制代码
     1.框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
     2.在框架的基础上构建软件编写更加高效、规范、通用、可扩展。

📖今日知识点 ------JDBC 与 MyBatis 对比

传统 JDBC 开发存在两大痛点,MyBatis 分别给出了对应的改进方案:

传统 JDBC 问题 具体表现 MyBatis 改进方案
硬编码 注册驱动、获取连接写死在代码里 将数据库连接信息提取到 mybatis-config.xml 中
硬编码 SQL 语句散落在 Java 代码中 将 SQL 语句提取到 Mapper 映射文件中
操作繁琐 手动设置参数 框架自动解析并设置参数
操作繁琐 手动封装结果集 框架自动将结果集映射为 Java 对象

简单来说:硬编码 → 配置文件,操作繁琐 → 自动完成。

💻练习代码

传统JDBC代码:

java 复制代码
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 获取Connection连接
String url = "jdbc:mysql:///db1?useSSL=false";
String uname = "root";
String pwd = "1234";
Connection conn = DriverManager.getConnection(url, uname, pwd);

// 3. 定义sql
String gender = "男";
String sql = "select * from tb_user where gender = ?";

// 4. 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);

// 5. 设置?的值
pstmt.setString(1, gender);

// 6. 执行sql
ResultSet rs = pstmt.executeQuery();

// 7. 封装结果集(手动映射)
User user = null;
ArrayList<User> users = new ArrayList<>();
while (rs.next()) {
    // 获取数据
    int id = rs.getInt("id");
    String username = rs.getString("username");
    String password = rs.getString("password");
    
    // 创建对象,设置属性值
    user = new User();
    user.setId(id);
    user.setUsername(username);
    user.setPassword(password);
    user.setGender(gender);
    
    // 装入集合
    users.add(user);
}

MyBatis 简化后的实现

  1. 配置文件(替代硬编码的连接信息)
java 复制代码
<!-- 连接池信息 -->
<dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///db1?useSSL=false"/>
    <property name="username" value="root"/>
    <property name="password" value="1234"/>
</dataSource>
  1. 映射文件(替代硬编码的SQL与手动封装)
java 复制代码
<select id="selectByGender" parameterType="string" resultType="com.itheima.pojo.User">
    select * from tb_user where gender = #{gender};
</select>
  1. Java 调用代码(极其精简)
java 复制代码
List<User> users = sqlSession.selectList("test.selectByGender", "男");

📖今日知识点 ------MyBatis快速入门

目标:查询 user 表中所有数据

操作步骤:

1. 创建 user 表,添加数据
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,'李四','234','女','天津');
   insert into tb_user values (3,'王五','11','男','西安');
2. 创建模块,导入坐标
pom.xml:
java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mybatis-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

        <!-- MySQL 驱动:升级至 8.2.0,修复 CVE-2023-22102 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.2.0</version>
        </dependency>

        <!-- 显式引入 protobuf-java 高版本,修复传递依赖 CVE-2024-7254 -->
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.25.5</version>
        </dependency>

        <!-- JUnit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!-- SLF4J API -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.36</version>
        </dependency>

        <!-- Logback:升级至 1.3.15,修复 CVE-2024-12798 等 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.3.15</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.3.15</version>
        </dependency>
    </dependencies>
</project>
3. 编写 MyBatis 核心配置文件

作用:替换连接信息,解决硬编码问题

mybatis -config.xml
java 复制代码
<?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://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 加载sql映射文件 -->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>
4. 编写 SQL 映射文件

作用:统一管理 SQL 语句,解决硬编码问题

UserMapper.xml:
java 复制代码
<?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: 名称空间
    select id:sql语句的唯一标识
    resultType:结果集的类型,对应pojo类名
-->
<mapper namespace="test">
    <select id="selectAll" resultType="com.itheima.pojo.User">
        select * from tb_user;
    </select>

</mapper>
5. 编码
复制代码
	1.定义 POJO 类
	2.加载核心配置文件,获取 SqlSessionFactory 对象
	3获取 SqlSession 对象,执行 SQL 语句
	4.释放资源
定义POJO类(User类)
java 复制代码
package com.itheima.pojo;
//alt + 鼠标左键 :整列编辑
public class User {
    //属性:
    private Integer id;
    private String username;
    private String password;
    private String gender;
    private String adder;

    //get/set方法:
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAdder() {
        return adder;
    }

    public void setAdder(String adder) {
        this.adder = adder;
    }
    //toString方法:
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender='" + gender + '\'' +
                ", adder='" + adder + '\'' +
                '}';
    }
}
java 复制代码
package com.itheima.pojo;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MybatisDemo {

    public static void main(String[] args) throws IOException {
        //1.加载mybatis的核心配置文件,获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3. 执行sql语句
        //参数:namespace.id
        List<User> users = sqlSession.selectList("test.selectAll");
        System.out.println(users);
        //4. 关闭SqlSession对象
        sqlSession.close();
    }
}

打印结果:

📖今日知识点 ------解决SQL语句警告提示

  • SQL语句警告:

  • 产生原因:Idea和数据库没有建立连接,不识别表的信息

  • 解决方式:在Idea配置MySQL数据库连接

    1.在右上角找到Database,点击加号,Data Source,MySQL。

    2.填写如下:

    3.可以在idea直接新建查询窗口

📖今日知识点 ------Mapper代理开发

  1. 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下。

    UserMapper接口和UserMapper.xml在同一目录下:

  2. 设置SQL映射文件的namespace属性为Mapper接口全限定名。

    将namespace改为UserMapper接口的全限定名:

  3. 在 Mapper 接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致

java 复制代码
package com.itheima.mapper;

import com.itheima.pojo.User;

import java.util.List;

public interface UserMapper {

    List<User> selectAll();
}
  1. 编码

    复制代码
    1.通过 SqlSession 的 getMapper方法获取 Mapper接口的代理对象
    
    2.调用对应方法完成sql的执行
java 复制代码
package com.itheima.pojo;

import com.itheima.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MybatisDemo2 {

    public static void main(String[] args) throws IOException {
        //1.加载mybatis的核心配置文件,获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3. 执行sql语句
        //参数:namespace.id
        //List<User> users = sqlSession.selectList("test.selectAll");

        //3.1获取UserMapper接口的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = userMapper.selectAll();
        System.out.println(users);
        //4. 关闭SqlSession对象
        sqlSession.close();
    }
}
  1. 细节:如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载:

运行结果:

📖今日知识点 ------Mybatis核心配置文件

MyBatis 核心配置文件(mybatis-config.xml)的顶层结构如下,<configuration> 是根节点,其余标签都是它的子节点:

配置标签 说明
configuration 配置文件的根节点,所有配置都写在其中
properties 属性,用于外部化配置(如数据库连接参数),可被其他配置引用
settings 设置,MyBatis 的全局行为开关(如驼峰映射、日志实现等)
typeAliases 类型别名,为 Java 类型设置简短别名,简化映射文件中的书写
typeHandlers 类型处理器,负责 Java 类型与 JDBC 类型之间的转换
objectFactory 对象工厂,用于创建结果集映射后的对象实例
plugins 插件,通过拦截器机制扩展 MyBatis 功能(如分页插件)
environments 环境配置,可配置多套环境(开发、测试、生产)
environment 环境变量,environments 的子节点,代表一套具体环境
transactionManager 事务管理器,配置事务管理方式(如 JDBC、MANAGED)
dataSource 数据源,配置数据库连接信息(如 POOLED、UNPOOLED、JNDI)
databaseIdProvider 数据库厂商标识,支持多数据库厂商的 SQL 适配
mappers 映射器,注册 SQL 映射文件或 Mapper 接口

注意:这些标签在配置文件中有严格的顺序要求,必须按照上表顺序书写,否则会报错。

📖今日知识点 ------Mybatis案例 --- 环境准备

  1. 数据库表 tb_brand

  2. 实体类 Brand

java 复制代码
package com.itheima.pojo;

public class Brand {
    // id 主键
    private Integer id;
    // 品牌名称
    private String brandName;
    // 企业名称
    private String companyName;
    // 排序字段
    private Integer ordered;
    // 描述信息
    private String description;
    // 状态:0: 禁用 1: 启用
    private Integer status;

    //get/set方法
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getBrandName() {
        return brandName;
    }

    public void setBrandName(String brandName) {
        this.brandName = brandName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public Integer getOrdered() {
        return ordered;
    }

    public void setOrdered(Integer ordered) {
        this.ordered = ordered;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }
    //toString方法

    @Override
    public String toString() {
        return "Brand{" +
                "id=" + id +
                ", brandName='" + brandName + '\'' +
                ", companyName='" + companyName + '\'' +
                ", ordered=" + ordered +
                ", description='" + description + '\'' +
                ", status=" + status +
                '}';
    }
}
  1. 测试用例

  2. 安装 MyBatisX 插件

    复制代码
    1.MyBatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
    
    2.主要功能:
    
    			XML 和 接口方法 相互跳转
    			
    			根据接口方法生成 statement
    3.安装:

打开UserMapper.xml和UserMapper.Java会有带着红色和蓝色头绳的小鸟

📖今日知识点 ------查询---查询所有结果&结果映射

  1. 编写接口方法:Mapper接口

    复制代码
    参数:无
    
    结果:List<Brand>
java 复制代码
package com.itheima.mapper;

import com.itheima.pojo.Brand;

import java.util.List;

public interface BrandMapper {
    /**
     * 查询所有品牌
     */
    public List<Brand> selectAll();

}
  1. 编写SQL语句:SQL映射文件:
java 复制代码
<?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: 名称空间
    select id:sql语句的唯一标识
    resultType:结果集的类型,对应pojo类名
-->
<mapper namespace="com.itheima.mapper.BrandMapper">

<!--
alt+enter 自动生成sql语句的唯一标识
-->

    <select id="selectAll" resultType="com.itheima.pojo.Brand">
        select * from tb_brand;
    </select>
</mapper>
  1. 执行方法,测试
java 复制代码
package com.itheima.test;

import com.itheima.mapper.BrandMapper;
import com.itheima.pojo.Brand;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisTest {
    @Test
    public void testSelectAll() throws IOException {
        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
        List<Brand> brands = brandMapper.selectAll();
        System.out.println(brands);
        //5.释放资源
        sqlSession.close();
    }
}

总而言之:Mybatis完成操作需要几步?三步:编写接口方法-->编写SQL->执行方法

💻练习代码

运行结果:

(但是有些没有封装,比如brandName,companyName等,因为pojo包里的Brand里命名属性时采用驼峰命名法,而数据库字段名为brand_name,company_name,不一样对应不上,所以不能自动封装。)

⬇️有3种手动封装的方法:

java 复制代码
<?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: 名称空间
    select id:sql语句的唯一标识
    resultType:结果集的类型,对应pojo类名
-->
<mapper namespace="com.itheima.mapper.BrandMapper">

<!--
alt+enter 自动生成sql语句的唯一标识
-->
        <!--
        数据库的字段名称 和 实体类的属性名称不一样时,不能自动封装数据
        解决方法:
        1.起别名:对数据库的字段起别名,与实体类的属性名称一致(缺点:每次查询都要定义一次别名
        sql片段:缺点:不灵活
        2.使用resultMap:
                 1.定义<resultMap>标签,用于映射数据库的字段和实体类的属性
                 2.在<select>标签中使用resultMap属性,指定使用哪个resultMap
        -->

    <!-- 起别名
        <select id="selectAll" resultType="com.itheima.pojo.Brand">

#                 select id,brand_name as brandName,company_name as companyName,ordered,description,status
#                 from tb_brand;
        </select>
 -->
            <!-- sql片段 -->
            <!--
            id:完成主键字段的映射
                column:表的列名
                property:实体类的属性名
            result:完成其他字段的映射
                column:表的列名
                property:实体类的属性名
            -->
    <!--<sql id="brand_column">
        id, brand_name as brandName, company_name as companyName, ordered, description, status
    </sql>

        <select id="selectAll" resultType="brand">
            select
                <include refid="brand_column" />
            from tb_brand;
       </select>
    -->

    <!-- 使用resultMap -->
             <resultMap id="brandResultMap" type="com.itheima.pojo.Brand">
            <result column="brand_name" property="brandName"/>
            <result column="company_name" property="companyName"/>

             </resultMap>

    <select id="selectAll" resultMap="brandResultMap">
        select * from tb_brand;
    </select>


</mapper>

📖今日知识点 ------查询---查看详情

  1. 编写接口方法:Mapper接口

    复制代码
    参数:id
    
    结果:Brand
java 复制代码
    /**
     * 查看详情:根据id查询品牌详情
     */
    Brand selectById(int id);
  1. 编写 SQL语句:SQL映射文件
java 复制代码
    <!--
          * 参数占位符:
              1. #{}:会将其替换为 ?,为了防止SQL注入
              2. ${}:拼sql。会存在SQL注入问题
              3. 使用时机:
                  * 参数传递的时候:#{}*
                  * 表名或者列名不固定的情况下:${} 会存在SQL注入问题

          * 参数类型:parameterType:可以省略
          * 特殊字符处理:
                     1. 转义字符:
                     2. CDATA区
      -->

    <!-- 查看详情:根据id查询品牌详情 -->
    <select id="selectById" resultMap="brandResultMap">
        select *
        from tb_brand where id
        <![CDATA[
          <
        ]]>
        #{id};
    </select>
  1. 执行方法,测试
java 复制代码
    //测试根据id查询品牌详情
    @Test
    public void testSelectById() throws IOException {
        //接收参数
        int id = 1;

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
        Brand brand = brandMapper.selectById(id);
        System.out.println(brand);
        //5.释放资源
        sqlSession.close();
    }

总结:

  1. 参数占位符:

    复制代码
     1.#{}:执行SQL时,会将#{}占位符替换为?,将来自动设置参数值
     
     2.${}:拼SQL。会存在SQL注入问题
     
     3.使用时机:
     
     		参数传递,都使用#{}
     		
     		如果要对表名、列名进行动态设置,只能使用${}进行sql拼接。
  2. parameterType:

    复制代码
     用于设置参数类型,该参数可以省略
  3. SQL语句中特殊字符处理:

    复制代码
     转义字符
     
     <![CDATA[内容]]>

📖今日知识点 ------查询 --- 条件查询

  1. 编写接口方法:Mapper接口

    复制代码
    参数:所有查询条件
    
    结果:List<Brand>
java 复制代码
 /**
     * 条件查询
     *     参数接收:
     *     SQL 参数占位符名称就是你在 XML 映射文件里写在 #{} 里面的那个名字。
     *            1.散装参数:如果方法中有多个参数,需要使用@param("SQL参数占位符名称")
     *            2,对象参数:对象属性名称要和参数占位符名称一致,否则会报错
     *            3,Map集合参数
     *
     *
     *
     *
     */
    //List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName);


    /**
     * 多条件查询 - 实体类封装参数
     */
    //List<Brand> selectByCondition(Brand brand);


    /**
     * 多条件查询 - Map集合封装参数
     */
    List<Brand> selectByCondition(Map map);
  1. 编写SQL语句:SQL映射文件
java 复制代码
    <!-- 条件查询 -->
    <select id="selectByCondition" resultMap="brandResultMap">
    select *
    from tb_brand
    where status = #{status}
    and company_name like #{companyName}
    and brand_name like #{brandName}
    </select>
  1. 执行方法,测试
java 复制代码
//测试条件查询
    @Test
    public void testSelectByCondition() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "华为";
        String brandName = "华为";

        //处理参数
        companyName = "%" + companyName + "%";
        brandName = "%" + brandName + "%";

        //封装成对象
//        Brand brand = new Brand();
//        brand.setStatus(status);
//        brand.setCompanyName(companyName);
//        brand.setBrandName(brandName);

          Map map = new HashMap();
          map.put("status",status);
          map.put("companyName",companyName);
          map.put("brandName",brandName);

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
//        List<Brand> brands = brandMapper.selectByCondition(status,companyName,brandName);
//        List<Brand> brands = brandMapper.selectByCondition(brand);
        List<Brand> brands = brandMapper.selectByCondition(map);
        System.out.println(brands);
        //5.释放资源
        sqlSession.close();
    }
  1. 总结:

    SQL语句设置多个参数有几种方式?

    复制代码
     1.散装参数:需要使用@Param("SQL中的参数占位符名称")
     
     2.实体类封装参数
     只需要保证SQL中的参数名 和 实体类属性名对应上,即可设置成功
     
     3.map集合
     只需要保证SQL中的参数名 和 map集合的键的名称对应上,即可设置成功

📖今日知识点 ------查询 --- 动态条件查询

一、查询-多条件-动态条件查询

  1. SQL语句会随着用户的输入或外部条件的变化而变化,我们称为 动态SQL

  2. MyBatis 对动态SQL有很强大的支撑:

    复制代码
    if:用于判断参数是否有值,使用test属性进行条件判断
    
    choose (when, otherwise)
    
    trim (where, set)
    
    foreach
java 复制代码
    <!--
    动态条件查询
        * if:条件判断
            * test:逻辑表达式
        * 问题:
            * 恒等式
            * <where> 替换 where 关键字
     -->
    <!-- 动态条件查询 -->
    <select id="selectByCondition" resultMap="brandResultMap">
        select *
        from tb_brand
        /* where 1 = 1*/
        <where>
            <if test="status != null">
                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>
  1. 存在的问题:第一个条件不需要逻辑运算符

    复制代码
     解决方案:
     1.使用恒等式让所有条件格式都一样
     2.<where>标签替换 where 关键字
java 复制代码
    //测试条件查询
    @Test
    public void testSelectByCondition() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "华为";
        String brandName = "华为";

        //处理参数
        companyName = "%" + companyName + "%";
        brandName = "%" + brandName + "%";

        //封装成对象
        Brand brand = new Brand();
        //brand.setStatus(status);
        //brand.setBrandName(brandName);
        brand.setCompanyName(companyName);/*
          Map map = new HashMap();
          map.put("status",status);
          map.put("companyName",companyName);
          map.put("brandName",brandName);
*/
        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
        List<Brand> brands = brandMapper.selectByCondition(brand);
//        List<Brand> brands = brandMapper.selectByCondition(brand);
//        List<Brand> brands = brandMapper.selectByCondition(map);
        System.out.println(brands);
        //5.释放资源
        sqlSession.close();
    }

二、查询-单条件-动态条件查询

  1. 从多个条件中选择一个

    复制代码
    choose (when, otherwise):选择,类似于Java中的 switch 语句

💻练习代码

1.Mapper接口:

java 复制代码
    //单条件,动态查询
    List<Brand> selectByConditionSingle(Brand brand);

2.编写SQL语句:SQL映射文件:

java 复制代码
     <!--动态单条件查询-->
    <select id="selectByConditionSingle" resultMap="brandResultMap">
        select *
        from tb_brand
        where
        <choose><!-- 相当于switch-->
            <when test="status != null">
                status = #{status}
            </when> <!-- 相当于case-->
            <when test="companyName != null and companyName != ''">
                and company_name like #{companyName}
            </when> <!-- 相当于case-->
            <when test="brandName != null and brandName != ''">
                and brand_name like #{brandName}
            </when> <!-- 相当于case-->
            <otherwise>
                1 = 1
            </otherwise>
        </choose>
    </select>

3.执行方法,测试:

java 复制代码
    //测试条件查询,单条件动态
    @Test
    public void testSelectByConditionSingle() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "华为";
        String brandName = "华为";

        //处理参数
        companyName = "%" + companyName + "%";
        brandName = "%" + brandName + "%";

        //封装成对象
        Brand brand = new Brand();
        brand.setStatus(status);
        //brand.setBrandName(brandName);
        //brand.setCompanyName(companyName);

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
//        List<Brand> brands = brandMapper.selectByCondition(brand);
//        List<Brand> brands = brandMapper.selectByCondition(brand);
//        List<Brand> brands = brandMapper.selectByCondition(map);

        List<Brand> brands = brandMapper.selectByConditionSingle(brand);
        System.out.println(brands);
        //5.释放资源
        sqlSession.close();
    }

📖今日知识点 ------添加

  1. 编写接口方法:Mapper接口

    复制代码
     代码框:void add(Brand brand);
     
     参数:除了id之外的所有数据
     
     结果:void
java 复制代码
    /**
     * 添加
     */
    void add(Brand brand);
  1. 编写 SQL语句:SQL映射文件
java 复制代码
    <!-- 添加 -->
    <insert id="add">
        insert into tb_brand(brand_name,company_name,ordered,description,status)
        values(#{brandName},#{companyName},#{ordered},#{description},#{status});
    </insert>
  1. 执行方法,测试

    复制代码
     MyBatis事务:
     
     openSession(): 默认开启事务,进行增删改操作后需要使用 sqlSession.commit(); 手动提交事务
     
     openSession(true): 可以设置为自动提交事务(关闭事务)
java 复制代码
    //添加
    @Test
    public void testAdd() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "波导手机";
        String brandName = "b波导";
        String description = "波导手机,手机中的战斗机";
        int ordered = 100;


        //封装成对象
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setBrandName(brandName);
        brand.setCompanyName(companyName);
        brand.setDescription(description);
        brand.setOrdered(ordered);

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
         brandMapper.add(brand);

        //提交.提交事务
        sqlSession.commit();
        //5.释放资源
        sqlSession.close();
    }
  1. 在数据添加成功后,需要获取插入数据库数据的主键的值,加useGeneratedKeys="true" keyProperty="id即可

    复制代码
     比如:添加订单和订单项
     
     		1.添加订单
     		
     		2.添加订单项,订单项中需要设置所属订单的id
java 复制代码
 <!-- 添加
    返回添加数据的主键
    useGeneratedKeys:开启自动获取主键值
    keyProperty:指定主键值的属性名
       -->
    <insert id="add" useGeneratedKeys="true" keyProperty="id">
        insert into tb_brand(brand_name,company_name,ordered,description,status)
        values(#{brandName},#{companyName},#{ordered},#{description},#{status});
    </insert>

📖今日知识点 ------修改功能

一、修改全部字段

  1. 编写接口方法:Mapper接口

    复制代码
     参数:所有数据
     结果:int(返回影响行数)
java 复制代码
    /**
     * 修改--全部字段
     */
    int update(Brand brand);
  1. 编写 SQL语句:SQL映射文件
java 复制代码
 <!-- 修改全部字段
          -->
    <update id="update">
        update tb_brand
        set brand_name = #{brandName},
        company_name = #{companyName},
        ordered = #{ordered},
        description = #{description},
        status = #{status}
        where id = #{id};
    </update>
  1. 执行方法,测试
java 复制代码
    //修改全部字段
    @Test
    public void testUpdate() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "苹果手机";
        String brandName = "苹果";
        String description = "苹果手机,苹果公司生产的一款智能设备";
        int ordered = 5000;
        int id = 5;


        //封装成对象
        Brand brand = new Brand();
        brand.setStatus(status);
        brand.setBrandName(brandName);
        brand.setCompanyName(companyName);
        brand.setDescription(description);
        brand.setOrdered(ordered);
        brand.setId(id);

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
        int count = brandMapper.update(brand);
        System.out.println(count);

        //提交.提交事务
        sqlSession.commit();
        //5.释放资源
        sqlSession.close();
    }
  1. 修改结果:

二、修改动态字段

  1. 编写接口方法:Mapper接口

    复制代码
    参数:部分数据,封装到对象中
    
    结果:int

    代码和修改全部字段的一样:

java 复制代码
int update(Brand brand);
  1. 编写 SQL语句:SQL映射文件
    加了if语句判断,和set标签
java 复制代码
<!-- 修改部分字段
       -->
    <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};
   **为什么这里要加 `<set>`?**

如果不加 `<set>`,直接写 `update tb_brand set ...`,会带来两个问题:

1. **逗号残留导致 SQL 语法错误**:`<if>` 是动态拼接的,如果最后一个生效的条件后面带着逗号(比如 `status = #{status},`),拼出来的 SQL 就会变成 `... status = ? , where id = ?`,多出一个逗号,直接报错。
2. **所有条件都不满足时 SQL 不合法**:如果传入的对象所有字段都为 null,`set` 后面就空了,变成 `update tb_brand where id = ?`,语法错误。

`<set>` 标签的作用就是**自动处理这两件事**:

- 它会自动在拼接内容前加上 `SET` 关键字;
- 它会**自动去掉最后一个多余的逗号**;
- 如果所有 `<if>` 都不成立,它不会生成 `SET` 关键字(此时 SQL 不合法,所以业务上至少要保证有一个字段被更新)。

所以上面代码里每个 `<if>` 内部都放心地写了逗号,交给 `<set>` 去裁剪,最终生成的 SQL 类似:

```sql
update tb_brand
set brand_name = ?, company_name = ?, ordered = ?
where id = ?;

补充:<set> 本质是 <trim prefix="SET" suffixOverrides=","> 的简写,和 <where> 标签是同一套思路------<where> 负责去掉多余的 AND/OR,<set> 负责去掉多余的逗号。
```

  1. 执行方法,测试
    注:记得把brand.setId(id);里的id改成具体值
java 复制代码
 //修改部分字段
    @Test
    public void testUpdate() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "苹果";
        String brandName = "ipad";
        String description = "ipad,苹果公司生产的一款智能平板";
        int ordered = 5000;
        int id = 5;


        //封装成对象
        Brand brand = new Brand();
        //brand.setStatus(status);
        brand.setBrandName(brandName);
        //brand.setCompanyName(companyName);
        brand.setDescription(description);
        //brand.setOrdered(ordered);
        brand.setId(5);

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
        int count = brandMapper.update(brand);
        System.out.println(count);

        //提交.提交事务
        sqlSession.commit();
        //5.释放资源
        sqlSession.close();
    }
  1. 执行结果
  2. 刷新表就能看到修改后的结果:

📖今日知识点 ------删除功能

一、删除单个

  1. 编写接口方法:Mapper接口

    复制代码
    参数:id
    
    结果:void
java 复制代码
    /**
     * 根据id删除--单个
     */
    void deleteById(int id);
  1. 编写 SQL语句:SQL映射文件
java 复制代码
    <!-- 根据id删除-单个 -->
    <delete id="deleteById">
        delete from tb_brand where id = #{id};
    </delete>
  1. 执行方法,测试
java 复制代码
    //根据id删除--单个
    @Test
    public void testDeleteById() throws IOException {
        //接收参数
        int id = 6;
        //封装成对象

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
        brandMapper.deleteById(id);

        //提交.提交事务
        sqlSession.commit();
        //5.释放资源
        sqlSession.close();
    }
  1. 删除结果:

二、批量删除

  1. 编写接口方法:Mapper接口

    复制代码
    参数:id数组
    
    结果:void
java 复制代码
    /**
     * 根据id删除--批量
     */
    void deleteByIds(@Param("ids") int[] ids);
  1. 编写 SQL语句:SQL映射文件
java 复制代码
    <!-- 根据id删除-批量 -->
    <!--
    Mybatis会将数组参数,封装成Map对象
    默认:array = 数组
    使用@Param注解改变map集合的默认key名称
    -->
    <delete id="deleteByIds">
        delete from tb_brand where id
        in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
                #{id}
        </foreach>
          ;
    </delete>
  1. 执行方法,测试
java 复制代码
    //根据id删除--批量
    @Test
    public void testDeleteByIds() throws IOException {
        //接收参数
        int[] ids = {5,3,2};
        //封装成对象

        //1.获取sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2. 获取SqlSession对象,用它来执行sql
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3.获取Mapper接口的代理对象
        BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
        //4.执行方法
        brandMapper.deleteByIds(ids);

        //提交.提交事务
        sqlSession.commit();
        //5.释放资源
        sqlSession.close();
    }
  1. 批量删除成功:

  2. 刷新表格批量删除成功:

  3. 💡 核心知识点解析: 标签

    这个标签非常强大,专门用来遍历集合或数组。

    复制代码
    collection="ids":指定要遍历的集合名称。对应接口里 @Param("ids") 起的名字。
    
    item="id":遍历出来的每一个元素,临时命名为 id。下面的 #{id} 就是取这个值。
    
    separator=",":每次循环拼接完后,用逗号隔开。
    
    open="(":拼接的 SQL 语句以左括号开始。
    
    close=")":拼接的 SQL 语句以右括号结束。

📖今日知识点 ------参数传递

一、概述:

MyBatis 接口方法中可以接收各种各样的参数,MyBatis 底层对于这些参数进行不同的封装处理方式。MyBatis 提供了 ParamNameResolver 类来进行参数封装。

二、 单个参数

当接口方法只有一个参数时,MyBatis 的处理方式如下:

  1. POJO 类型:直接使用,要求实体类属性名和参数占位符名称一致。

  2. Map 集合:直接使用,要求键名和参数占位符名称一致。

  3. Collection 集合:封装为 Map 集合。

    复制代码
     map.put("collection", collection集合);
     
     map.put("arg0", collection集合);
  4. List 集合:封装为 Map 集合。

    复制代码
     map.put("collection", list集合);
     
     map.put("list", list集合);
     
     map.put("arg0", list集合);
  5. Array 数组:封装为 Map 集合。

    复制代码
     map.put("array", 数组);
     
     map.put("arg0", 数组);
  6. 其他类型:直接使用。

三、 多个参数

当接口方法有多个参数时,MyBatis 会将它们封装为一个 Map 集合:

复制代码
map.put("arg0", 参数值1);

map.put("param1", 参数值1);

map.put("arg1", 参数值2);

map.put("param2", 参数值2);

四、推荐最佳实践:使用 @Param 注解

💡 核心建议: 将来都使用 @Param 注解来修改 Map 集合中默认的键名,并使用修改后的名称来获取值,这样可读性更高!

示例:

java 复制代码
User select(@Param("username") String username, @Param("password") String password);

对应的 XML 映射:

java 复制代码
<select id="select" resultType="user">
  select *
  from tb_user
  where
    username = #{username}
    and password = #{password};
</select>

使用 @Param 后,底层 Map 的封装变为:

复制代码
map.put("username", 参数值1);

map.put("param1", 参数值1);

map.put("arg1", 参数值2);

五、 思考题 / 场景分析

代码示例:

java 复制代码
String username = "zhangsan";
String password = "123";

Map map = new HashMap();
User user = new User();
user.setUsername(username);
user.setPassword(password); 
map.put("user", user);

User user = userMapper.select(map);

对应的 XML 查询:

java 复制代码
<select id="select" resultType="user">
  select *
  from tb_user
  where
    username = #{?}
    and password = #{?}
</select>

思考:当传入的是一个 Map 集合,且 Map 中包含一个 User 对象时,XML 中的占位符该如何正确填写?(答案:#{user.username} 和 #{user.password})

📖今日知识点 ------注解开发---注解完成增删改查

核心观点:使用注解开发会比配置文件开发更加方便。

一、 基本用法

直接在 Mapper 接口方法上使用注解编写 SQL 语句。

代码示例:

java 复制代码
@Select("select * from tb_user where id = #{id}")
public User selectById(int id);

二、 常用 CRUD 注解

MyBatis 提供了以下四个基础注解对应数据库的增删改查操作:

复制代码
查询:@Select

添加:@Insert

修改:@Update

删除:@Delete

三、 使用建议与提示

💡 提示:

复制代码
注解完成简单功能

配置文件(XML)完成复杂功能

四、官方说明解读:

使用注解来映射简单语句会使代码显得更加简洁。

但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

灵活选择:选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。换句话说,永远不要拘泥于一种方式,你可以很轻松地在基于注解和 XML 的语句映射方式间自由移植和切换。

相关推荐
可乐ea21 分钟前
智能体记忆该拆成几层:笔记、记忆表示与注入上下文
服务器·数据库·笔记·ai智能体·提示词工程·上下文工程·记忆系统
数据技术说1 小时前
在 KES AI 数据库上搭一条 NL2SQL 链路:语义、召回、权限各放哪
人工智能·sql·代码规范
wang_shu_mo_ran3 小时前
MyBatis 从入门到进阶(一)
java·spring·mybatis
Go Work4 小时前
两个等值条件摆着,MySQL 偏要全表扫
经验分享·笔记
瑞雪兆丰年兮4 小时前
JavaWeb-day13-SpringBootWeb事务管理&AOP
java·开发语言·springboot·javaweb·事务·web·aop
Oll Correct4 小时前
Adobe illustrator 案例十四:花元素标志草图绘制
笔记·ui·adobe·illustrator
北京云帆互联科技6 小时前
云帆培训考试系统更新说明:v9.3.0
java·学习
sxlishaobin8 小时前
Maven Setting详解
java·学习·maven
瑞雪兆丰年兮8 小时前
JavaWeb-day14-SpringBootWeb原理篇
java·开发语言·spring boot·spring·bean·mybatis·javaweb