MyBatis

60 阅读6分钟

什么是 MyBatis ?

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

简单来说 MyBatis 是更简单完成程序和数据库交互的⼯具,也就是更简单的操作和读取数据库⼯具。 官网:mybatis – MyBatis 3 | 简介

image.png

第一个 MyBatis 查询

image.png

MyBatis 是在数据持久层通过接口 + .xml 文件的方式来实现对数据库的查询并返回数据。

1.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);

2.2 添加 MyBatis 框架支持

添加 MyBatis 框架:

image.png

添加需要操作的数据库驱动:

image.png

2.3 配置连接字符串和 MyBatis

配置数据库连接:

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/mycnblog2023?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

配置 MyBatis:

#设置MyBatis
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml

2.4 使用 MyBaits 进行开发

创建 mybatis 包:

image.png

创建实体类(和数据库中的表结构对应):

@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;
}

创建接口:

@Mapper
public interface UserMapper {
    List<UserInfo> getAll();
}

与其对应的xml文件:

image.png

<?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">
    <select id="getAll" resultType="com.example.demo.entity.UserInfo">
        select * from userinfo;
    </select>
</mapper>

image.png

小插件介绍,图示插件可以实现接口和 xml 文件之间的跳转:

image.png

创建service 和 controller, 并启动项目进行结果验证:

image.png

image.png

运行结果:

image.png

2.4 单元测试

单元测试(unit testing),是指对软件中的最⼩可测试单元进⾏检查和验证的过程就叫单元测试。

单元测试的好处:

1、可以⾮常简单、直观、快速的测试某⼀个功能是否正确。

2、使⽤单元测试可以帮我们在打包的时候,发现⼀些问题,因为在打包之前,所以的单元测试必须通过,否则不能打包成功。

3、使⽤单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进⾏任何改变的情况下,测试功能。

使用 MyBatis 完成从数据库查询 id 为 1 的用户,并使用单元测试进行验证:

image.png

@Param 注解用来指定传递的参数,用于和xml文件中要获取的参数对应。

image.png

动态获取参数的说明:

image.png

直接替换的方式容易引起网络安全问题,如sql注入等。

打印 MyBatis 执行中的sql:

#打印 mybatis 执行的sql 其默认的日志级别为debug,因此需要更改日志级别
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
logging.level.com.example.demo=debug

查看两种参数获取的对比:

image.png

image.png

单元测试:

在要进行测试的类或方法上鼠标右键,点击generate:

image.png

选择test:

image.png

选择需要进行单元测试的方法,其余不用改动:

image.png

在生成的测试代码中补充测试逻辑:

image.png

点击运行,查看结果:

image.png

2.5 传递一个对象进行查询

image.png

image.png

在测试方法中实现sql注入:

image.png

在没有输入用户名和密码的情况下,获取到了用户信息,因此要尽量使用#{}来获取数据,必须使用${}获取数据时,一定要保证所传数据是安全的。

2.6 实现单表的数据更新操作

image.png

update 默认返回受影响的行数,因此不用设置返回值

image.png

测试代码:

image.png

添加注解,不污染数据库,只是进行测试,其会在测试开始时开启事务,测试完成后进行回滚。

image.png

2.7 删除单表中的一条数据

image.png

delete 和 update 一样,默认返回一个受影响的行数:

image.png

测试结果:

image.png

2.8 单表中添加一条数据

  1. 返回添加受影响的行数

image.png

image.png

image.png

  1. 返回添加受影响的行数和新添加的id

image.png

image.png

image.png

2.9 模糊查询

当我们以图示方法查询时会报错:

image.png

通过sql 提供的concat() 函数来实现该部分查询:

image.png

修改查询代码:

image.png

运行结果:

image.png

resultType 和 resultMap 的区别:

当数据库中的字段和对象中的属性名一一对应,名字一样时使用 resultType 框架会自动实现映射,当不一致时,应该使用 resultMap 来实现映射:

1) 将类中的密码属性进行修改(数据库中为password):

image.png

2)查询结果:

image.png

修改返回值类型:

image.png

运行结果:

image.png

3.0 多表联合查询

查询文章详情及文章对应的作者:

数据库中的表结构:

image.png

创建对应的实体类:

image.png

多表联合查询结果对应的类:

image.png

创建对应的 mapper 和 xml :

image.png

image.png

运行结果:

image.png

动态 SQL

动态 SQL 是一种在编写 SQL 查询时能够根据不同条件或情况生成不同 SQL 语句的技术。通常,在编写静态 SQL 时,查询语句的结构和参数都是固定的,而动态 SQL 允许根据需要动态地构建和修改查询语句。

示例1(标签if):

向数据库中插入一条用户信息,当photo为空时,不插入photo,其不为空时,插入photo:

image.png

没有 photo 时的运行结果:

image.png

有 photo 时的运行结果:

image.png

示例2(trim标签):

trim 标签中有如下属性:

prefix:表示整个语句块,以prefix的值作为前缀

suffix:表示整个语句块,以suffix的值作为后缀

prefixOverrides:表示整个语句块要去除掉的前缀

suffixOverrides:表示整个语句块要去除掉的后缀

当示例1中的三个属性都可能为空时,修改代码:

image.png

示例3(where标签):

当需要根据传入的id 和 title 进行文章查询时,where 语句中的条件可能存在,可能不存在,需要特殊处理(建议使用方法3):

image.png

示例4(set标签):

更新用户3的用户名和密码:

image.png

image.png

示例5(foreach标签):

image.png

一次删除多篇文章:

image.png

image.png