• MyBatis配置及单表操作


    一. MyBatis概述

    MyBatis 是一款优秀的持久层框架,它支持自定义 SQL存储过程以及高级映射;MyBatis 去除了几乎所有的 JDBC 代码和动设置参数以及获取结果集的工作;MyBatis可以使用简单的 XML 或注解来配置和映射原始类型,将接口和 Java 的 POJO(Plain Old Java Objects,普通老式的 Java 对象)映射成数据库中的记录。

    简单来说 MyBatis 其实就是对JDBC进行了封装,它是更简单的完成程序和数据库交互的⼯具,也就是更简单的操作和读取数据库工具。

    所以我们使用 MyBatis 的时候需要创建 Mapper(Dao)层接口以及对应的xml文件,也可以使用注解代替xml文件进行写 SQL 的方式,但是 SQL 语句复杂的时候使用注解这种方式不太好实现且不太美观

    MyBatis 在整个框架中的定位交互如下图:

    img

    MyBatis 也是⼀个 ORM 框架,ORM(Object Relational Mapping),即对象关系映射;在面向
    对象编程语⾔中,将关系型数据库中的数据与对象建立起映射关系,进而自动的完成数据与对象
    的互相转换:

    1. 将输⼊数据(即传入对象)+ SQL 映射成原⽣ SQL
    2. 将结果集映射为返回对象,即输出对象

    ORM 把数据库映射为对象:

    • 数据库表(table)–> 类(class)
    • 记录(record,行数据)–> 对象(object)
    • 字段(field) --> 对象的属性(attribute)

    ⼀般的 ORM 框架,会将数据库模型的每张表都映射为⼀个 Java 类,也就是说使⽤ MyBatis 可以像操作对象⼀样来操作数据库中的表,可以实现对象和数据库表之间的转换。

    二. MyBatis项目的创建

    1. 准备一个数据表

    这里创建以下数据库和数据表。

    -- 创建数据库
    drop database if exists mycnblog2023;
    create database mycnblog2023 DEFAULT CHARACTER SET utf8mb4;
    
    -- 使用数据数据
    use mycnblog2023;
    
    -- 创建表[用户表]
    drop table if exists  userinfo;
    create table userinfo(
        id int primary key auto_increment,
        username varchar(100) not null,
        password varchar(32) not null,
        photo varchar(500) default '',
        createtime timestamp default current_timestamp,
        updatetime timestamp default current_timestamp,
        `state` int default 1
    ) default charset 'utf8mb4';
    
    -- 创建文章表
    drop table if exists  articleinfo;
    create table articleinfo(
        id int primary key auto_increment,
        title varchar(100) not null,
        content text not null,
        createtime timestamp default current_timestamp,
        updatetime timestamp default current_timestamp,
        uid int not null,
        rcount int not null default 1,
        `state` int default 1
    )default charset 'utf8mb4';
    
    -- 创建视频表
    drop table if exists videoinfo;
    create table videoinfo(
      	vid int primary key,
      	`title` varchar(250),
      	`url` varchar(1000),
    		createtime timestamp default current_timestamp,
    		updatetime timestamp default current_timestamp,
      	uid int
    )default charset 'utf8mb4';
    
    -- 添加一个用户信息
    INSERT INTO `mycnblog2023`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES 
    (1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);
    
    -- 文章添加测试数据
    insert into articleinfo(title,content,uid)
        values('Java','Java正文',1);
        
    -- 添加视频
    insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    下面的内容主要是对userinfo表进行操作演示,目前执行上述 sql 后,表内的数据如下:
    img

    所以我们针对我们要操作的数据表,还应该有对应的映射类(userinfo 表对应一个 UserInfo 类)。

    package com.example.demo.model;
    
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    public class UserInfo {
        private int id;
        private String username;
        private String password;
        private String photo;
        private LocalDateTime createtime;
        private LocalDateTime updatetime;
        private int state;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2. 创建项目

    1️⃣第一步,创建 SpringBoot 项目。
    img

    2️⃣第二步,勾选上 MyBatis 相关依赖,包括Mybatis Framework(一定不能少)和MySQL Driver(看你使用的是哪个数据库就添加哪个)。

    img
    3️⃣第三步,项目创建好后此时如果我们直接启动项目是会报错的,我们还需要在application配置文件中配置数据库数据源和 MyBatis xml文件保存路径,其中这个xml文件是用来编写sql语句用的。

    🍂先配置数据库链接信息,需要配置数据库的 url,账号与密码以及驱动。

    .properties文件格式:

    # 设置数据库的相关连接信息
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mycnblog2023?characterEncoding=utf8
    spring.datasource.username=root
    spring.datasource.password=111111
    # 版本8之前版本的数据库使用 com.mysql.jdbc.Driver
    # 版本8以及之后版本的数据库使用 com.mysql.cj.jdbc.Driver
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    .yml文件格式:

    spring:
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8
        username: root
        password: 111111
        driver-class-name: com.mysql.cj.jdbc.Driver
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🍂接下来设置 MyBatis xml 文件的路径:

    .properties文件格式:

    # 设置 MyBatis XML 存放路径和命名格式
    mybatis.mapper-locations=classpath:mybatis/*Mapper.xml
    
    • 1
    • 2

    .yml文件格式:

    mybatis:
      mapper-locations: classpath:mybatis/*Mapper.xml
    
    • 1
    • 2

    其中*Mapper.xml表示只认Mapper.xml结尾的文件,存放在资源文件(resources)的mybatis路径下。

    🍂如果需要更方便观察有关 sql 的运行情况,还可以配置一些日志文件,来显示 sql 相关的日志(debug级别,默认是不显示的):

    .properties文件格式:

    # 配置 MyBatis 执行时打印 SQL(可选配置)
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    logging.level.com.example.demo=debug
    
    • 1
    • 2
    • 3

    .yml文件格式:

    logging:
      level:
        com:
          example:
            demo: debug
    mybatis:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这样我们的项目就创建好了,下面的内容介绍 MyBatis 的基本使用。

    三. MyBatis的使用

    1. 基本使用

    MyBatis 的基本使用流程:

    1. 创建一个使用注解@Mapper修饰的接口(用来给 Service 层调用),该注解来自 MyBatis,作用是与我们配置的Mapper.xml结尾的文件做连接。
    2. 在该接口里面定义方法。
    3. 在 xml 文件中按照 MyBatis 的规则编写 sql(接口的具体实现),xml 文件的作用是生成数据库可执行的 sql,并且能将结果映射到程序的对象中。

    1️⃣第一步,我们定义接口,该接口在软件分层中属于数据持久层,所以我们定义在自建的dao包下(也可定义为 mapper),为了提高代码的可读性和规范性,接口名称建议Mapper结尾。

    @Mapper
    public interface UserMapper {
    	//方法声明
    }
    
    • 1
    • 2
    • 3
    • 4

    2️⃣第二步,我们在该接口下声明方法,由于该方法的目的就是为了被xml文件“实现”,然后去操作数据库的,比如我们需要所有查询User对象的结果,我们声明一个getAll方法。

    // 查询全部用户信息
    List<UserInfo> getAll();
    
    • 1
    • 2

    3️⃣第三步,在配置文件所规定的目录下创建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="com.example.demo.mapper.UserMapper">
    
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    当我们要编写 sql 时,需要在标签里面写对应的标签来实现对数据库的操作,比如查询使用select标签,插入使用insert标签,更新使用pudate标签,删除使用delete标签等。

    而在mapper标签中,我们需要设置该mapper标签对应的接口在哪里,就是在mapper标签头中设置namespace属性,值为加包名的Mapper接口,如:

    <mapper namespace="com.example.demo.mapper.UserMapper">
    </mapper>
    
    • 1
    • 2

    比如要查询全部用户信息,其中标签头的id属性与要实现的方法名对应,resultType表示需要映射的类是哪一个,需要写出完整的包名,这样就会将查询的结果存入到该类的对象中对对象数组中,有一点是需要注意的,那就是使用resultType映射对象一定得保证数据库中的字段名与对象中的属性名一模一样,否则不能匹配赋值。

    <select id="getAll" resultType="com.example.demo.model.UserInfo">
        select * from userinfo
    </select>
    
    • 1
    • 2
    • 3

    2. SpringBoot单元测试

    完成上面的工作后我们可以使用单元测试验证我们写的代码是否符合预期。

    1️⃣第一步,我们在类中代码右键,选择Generate选项,比如我们在 Mapper 接口中生成单元测试的类,我们就在右键选择Generate选项。
    img
    然后会跳出一个框,选择 Test。
    img

    2️⃣第二步,进行配置 Test,和选择需要测试的方法。
    img

    3️⃣第三步,第二步勾选上需要测试的方法后,会在 test 目录下生成一个同级的目录,会自动生成测试类和测试方法,当然我们也能够手动创建,创建的测试类就在原来类名最后加上一个Test即可。

    单元测试的类创建在如图目录下:
    img

    再完成以下步骤:

    • 配置单元测试的类,在类上加上@SpringBootTest注解,这个一定不要忘记,它的作用是告诉当前的测试程序,当前的项目是运行在 SpringBoot 容器中。
    • 添加测试方法,并加上@Test注解
    • 通过注入 Mapper 对象,调用里面操作数据库的方法,进行单元测试。
      img
    • 执行单元测试,测试通过结果如下:
      img

    四. 使用MyBatis实现单表操作

    上面已经创建好了 Mapper 接口和对应的 xml 文件了,此时我们就可以开始写数据库 sql 的语句了,先介绍基础使用,完成对单表的 CRUD 操作。

    1. 查询

    1️⃣第一步,在 Mapper 接口中声明一个方法,比如通过用户id查询用户信息,就可以声明一个方法getUserById

    UserInfo getUserById(@Param(value = "id") Integer id);
    
    • 1

    @Param注解中的value参数值表示对变量重命名,此时传入到 xml 文件中的变量名字就是设置的这个值,建议该名字与数据库中的字段相对应并同名。

    2️⃣第二步,设置 xml 文件所映射的 Mapper 接口,假设我们映射了一个如下图dao包中的UserMapper接口,该接口一定要使用@Mapper修饰,否则就不是属于 MyBatis 中的“接口”,不能与 xml 产生映射。

    img
    我们将mapper标签中的namespace设置为对应接口在项目目录下的带包全称。

    <mapper namespace="com.example.demo.mapper.UserMapper">
    
    • 1

    3️⃣第三步,设置查询标签以及配置与方法的映射,查询操作使用select标签,查询标签至少设置两个属性,第一个是id表示与哪一个方法相对应,就是设置成相关联的方法的方法名,比如我们在 Mapper 接口中的查询方法是getUserById,则设置id="getUserById"

    然后设置resultType属性的值,我们查询需要返回查询结果UserInfo对象,如果可能有多个结果,对应的方法返回值就写List,否则写UserInfo即可,resultType属性中只需要设置成带包名的类名即可。

    <select id="getUserById" resultType="com.example.demo.model.UserInfo">
    
    • 1

    4️⃣第四步,在 mapper 标签中编写查询 sql,查询 sql 对应的标签就是select标签,如我们要查询id1的用户,那么标签里写的内容如下:

    <select id="getUserById" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where id=1
    </select>
    
    • 1
    • 2
    • 3

    但是这样写太 sql 就写死了,代码没有通用性,我们可以使用预处理符#{}或者替换符${}来将程序传入的参数替换到 sql 语句中:

    <select id="getUserById" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where id=${id}
        //or select * from userinfo where id=#{id}
    </select>
    
    • 1
    • 2
    • 3
    • 4

    其中id=${id}相当于id=传入的id变量的值,比如id=1,则${id}会被替换为1,使用该方法存在 SQL 注入的问题,并且传入字符串等其他非数值类型可能会出现问题,而id=#{id}相当于id=?,相当于替换成一个占位符,然后会将传入的id通过占位符的形式插入到 sql 语句中,可以防止 SQL 注入问题,并且适用于所有类型的变量,关于这两者更多的介绍会在下一篇多表查询中介绍。

    经过以上步骤我们的 sql 就写好了,由于我们的id是唯一的,所以查询到的结果也是唯一的,我们以id=1进行查询演示,目前数据库的数据如下:
    img
    🎯然后我们再写单元测试代码进行验证:

    @Test
    void getUserById() {
        UserInfo userinfo = userMapper.getUserById(1);
        System.out.println(userinfo.toString());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果如下:

    img

    2. 修改

    我们在 userinfo 表中再插入一条记录,此时表中数据如下:
    img
    现在我们演示如何使用 MyBatis 根据id修改对应用户名username,其实和查询操作也差不多,默认返回受影响的行数,是基础类型,此时我们是不用设置resultType的。

    1️⃣第一步,声明方法:

    // 根据 id 修改用户名
    Integer updateName(@Param(value = "id") Integer id, @Param(value = "username") String username);
    
    • 1
    • 2

    2️⃣第二步,在 xml 中写 sql,修改操作使用update标签。

    <update id="updateName" >
        update userinfo set username=#{username} where id=#{id}
    </update>
    
    • 1
    • 2
    • 3

    我们将id=2的用户名修改为张三,生成单元测试:

    @Test
    void updateName() {
        int result = userMapper.updateName(2, "张三");
        System.out.println("受影响行数: " + result);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果如下:
    img

    再来验证一下数据库中的数据是否完成了修改,

    img

    结果是符合我们的预期的,实际上单元测试是不应该去影响数据库中的数据的,我们可以在单元测试方法上使用@Transactional注解,这样就可以防止污染数据库,实现原理是利用了数据库事务的回滚,比如:

    img

    执行单元测试:

    img

    此时数据库是没有发生修改的,原因是@Transactional注解进行了事务的回滚,但是自增主键不会进行回滚,比如你进行了id3用户插入操作,事务回滚了,下一次插入自增主键id的值为4
    img
    这也是使用单元测试不会污染数据库的原因所在。

    3. 删除

    删除其实和查询,修改也是一样的,我们来演示将id2的数据删除。
    1️⃣第一步,声明方法:

    // 根据id删除用户
    public Integer delById(@Param(value = "id") Integer id);
    
    • 1
    • 2

    2️⃣第二步,在 xml 文件编写 sql,删除操作使用delete标签:

    <delete id="delById">
        delete from userinfo where id=#{id}
    </delete>
    
    • 1
    • 2
    • 3

    3️⃣第三步,编写单元测试验证:

    @Test
        void delById() {
            int result = userMapper.delById(2);
            System.out.println("受影响行数: " + result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4️⃣第四步,查询单元测试结果,验证数据库:
    imgimg

    4. 新增

    该操作相比于之前的查询,修改和删除操作要复杂一点,但其实也差不多,为了提高代码的通用性,我们声明方法中的形参传入一个UserInfo对象。

    首先,我们来演示插入一条数据,返回一个受影响的行数。

    1️⃣第一步,声明方法:

    // 添加用户,返回影响行数
    Integer add(UserInfo userInfo);
    
    • 1
    • 2

    2️⃣第二步,编写 sql 语句,插入操作使用insert标签。

    <insert id="add">
        insert into userinfo(username, password, photo) values (#{username}, #{password},#{photo});
    </insert>
    
    • 1
    • 2
    • 3

    3️⃣第三步,编写并执行单元测试,验证数据库。

    @Test
    void add() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("老六");
        userInfo.setPassword("666");
        int result = userMapper.add(userInfo);
        System.out.println("受影响行数: " + result);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    单元测试结果:
    img
    数据库查询结果:

    img
    🎯其实,Mybatis 也可以返回多个参数,比如返回受影响的行数和自增id的值,这个时候我们就需要对xmlinsert标签进行配置。

    前面声明方法还是需要的:

    Integer addGetId(UserInfo userInfo);
    
    • 1

    xml 的insert标签中还需要设置useGeneratedKeys="true"表示为是否自增主键,keyProperty="id"表示自增主键是哪一个字段。

    <insert id="addGetId" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        insert into userinfo(username, password, photo) values (#{username}, #{password},#{photo});
    </insert>
    
    • 1
    • 2
    • 3

    单元测试方法:

    @Test
    void addGetId() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("老八");
        userInfo.setPassword("888");
        userInfo.setPhoto("default.png");
        System.out.println("插入之前的id=" + userInfo.getId());
        int result = userMapper.addGetId(userInfo);
        System.out.println("插入之后的id=" + userInfo.getId());
        System.out.println("受影响行数: " + result);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    单元测试结果:
    img

    数据库查询结果:
    img

    五. 基于注解完成SQL

    这个其实很简单,使用配置文件我们需要在指定路径创建配置文件,实现具体接口的执行 sql;而使用注解实现 sql,直接在对应接口上面加上对应操作的注解,括号里面的属性填好 sql 属性即可。

    对应操作注解也就对应 SQL 的增删查改,分别为:

    • @Select,查询
    • @Update,更新/修改
    • @Delete,删除
    • @Insert,插入/新增

    我们来简单针对数据表演示一个查询操作,

    1️⃣第一步,声明一个方法getAllUser()

    // 基于注解查询全部用户
    List<UserInfo> getAllUser();
    
    • 1
    • 2

    2️⃣第二步,使用@Select实现具体的 sql 操作。

    img

    🎯单元测试代码:

    @Test
    void updateName() {
        int result = userMapper.updateName(2, "张三");
        System.out.println("受影响行数: " + result);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果如下:

    img
    其他注解使用起来也是一样的,就不在这里赘述了。

  • 相关阅读:
    网页元素定位秘籍:从HTML探秘到Python自动化实战20240626
    QT基础教程(QT中的文件操作)
    flutter 加密安全
    LeetCode每日一题(2447. Number of Subarrays With GCD Equal to K)
    20PIN直插百兆网络变压器 H82022D
    深度学习入门(五十四)循环神经网络——文本预处理
    新零售SaaS架构:什么是线上商城系统?
    腾讯云4核8G服务器选CVM还是轻量比较好?价格对比
    代码签名证书适用于个人开发者吗?
    HDLbits:Fsm onehot
  • 原文地址:https://blog.csdn.net/Trong_/article/details/132723722