Mybatis入门与基础操作详解

目录

一、什么是Mybatis

二、Mybatis入门

1. 准备工作

1.1 创建工程

1.2 数据准备

2. 配置数据库连接字符串

3. 写持久层代码

4. 单元测试

三、Mybatis基础操作

1. 打印日志

2. 查(Select)

2.1 起别名

2.2 结果映射

2.3 开启驼峰命名(推荐)

3. 参数传递

4. #{ } 和 ${ }

4.1 #{ } 和 ${ } 的使用

4.1.1 Integer类型的参数

4.2.2 String类型的参数

4.2 #{ } 和 ${ } 的区别

4.2.1 性能更高

4.2.2 更安全(防止SQL注入)

5. 增(Insert)

6. 删(Delete)

7. 改(Update)

四、Mybatis XML配置文件

1. 配置连接字符串和Mybatis

2. 写持久层代码

2.1 添加Mapper接口

2.2 添加UserInfoXMLMapper.xml

2.3 单元测试

3. 增删改查操作

3.1 增(Insert)

3.2 删(Delete)

3.3 改(Update)

五、其他查询操作

1. 多表查询

1.1 数据准备

1.2 数据查询

2. 排序功能

3. 模糊查询(Like)

六、数据库连接池

七、动态SQL

1. 标签

2. 标签

3. 标签

4. 标签


一、什么是Mybatis

MyBatis 是一款优秀的持久层框架,用于简化数据库操作。它通过 XML 或注解的方式将 Java 对象与数据库表中的记录进行映射,避免了手动编写 JDBC 代码的繁琐过程。MyBatis 支持定制化 SQL、存储过程以及高级映射,同时保持了良好的灵活性和性能。

Mybatis本是Apache的一个开源项目iBatis,2010年这个项目有Apache迁移到了google code,并改名为Mybatis,2013年11月迁移到Github。

中文官网

持久层

持久层指的就是持久化操作的层,通常指数据访问层(Dao),是用来操作数据库的。

二、Mybatis入门

Mybatis操作数据库的步骤:

  1. 准备工作:创建Sprintboot工程、数据库表准备、实体类
  2. 引入Mybatis的相关依赖,配置Mybatis数据库连接信息
  3. 编写SQL语句(注解/XML)
  4. 测试

1. 准备工作

1.1 创建工程

创建springboot工程,并导入mybatis的起步依赖和mysql的驱动包(Mybatis是一个持久层框架,具体的数据存储和数据操作还是在MySQL中操作的,所以需要添加MySQL驱动)。

SpringBoot版本和Mybatis版本对应参考:https://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/

1.2 数据准备

创建用户表和对应的实体类UserInfo:

-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;

-- 使用数据库
USE mybatis_test;

-- 创建用户表
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (
    `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
    `username` VARCHAR ( 127 ) NOT NULL,
    `password` VARCHAR ( 127 ) NOT NULL,
    `age` TINYINT ( 4 ) NOT NULL,
    `gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-女 0-默认',
    `phone` VARCHAR ( 15 ) DEFAULT NULL,
    `delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
    `create_time` DATETIME DEFAULT now(),
    `update_time` DATETIME DEFAULT now() ON UPDATE now(),
    PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;

-- 添加用户信息
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

UserInfo:字段名和属性名要一一对应

import lombok.Data;
import java.util.Date;

@Data
public class UserInfo {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

规范:

数据库字段:全部小写,单词之间使用下划线(_)分割

Java属性:使用小驼峰来表示

代码结构一定要体现出MVC架构:

  • Controller层 (表现层/控制层)
  • Service层 (业务逻辑层)
  • Mapper层 (数据访问层/持久层)
  • Model层 (模型层/实体层)

2. 配置数据库连接字符串

Mybatis中要连接数据库,需要数据库相关参数(MySQL驱动类,登录名,密码,数据库连接字符串)配置。

application.yml:

# 数据库连接配置
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root     
    password: root      # 这里写自己的MySQL用户名和密码
    driver-class-name: com.mysql.cj.jdbc.Driver

如果自己的密码是例如123456的数字的话,记得加上双引号 password:"123456" 

注意事项:如果使用MySQL是5.x之前的使用的是“com.mysql.jdbc.Driver”,如果是大于5.x使用的是"com.mysql.cj.jdbc.Driver"。

IDEA数据库可视化:

配置完成后,在代码左边会出现这样的符号:

点击,会弹出这样的界面,填上自己的用户名和密码,其他保持默认:

点击OK,编译器右侧工具栏的Database就会出现我们配置的MySQL,刚才新建的mybatis数据库和user_info数据表:

可视化:

点击“+”号,Query Console 还可以新建查询:

测试一下:

(学会这招,就不用在IDEA和Navicat之间来回切换了哈哈😄)

3. 写持久层代码

在项目中创建持久层接口UserInfoMapper:

import com.bite.mybatisrevisecode.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserInfoMapper {

    @Select("select * from user_info")
    List<UserInfo> selectAllUser();
}

Mybatis的持久层接口规范一般都叫XxxMapper。

@Mapper注解:表示的是Mybatis中的Mapper接口。

程序运行时,框架会自动生成接口的实现类对象(代理对象),并交给Spring的IOC容器管理。

@Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容。

接口的实现有两种方式:注解 / XML。这里使用的是注解的方式,XML方法后文会介绍到。

4. 单元测试

在创建出来的SpringBoot工程中的test目录下,已经自动帮我们创建好了测试类,我们可以直接用这个测试类来进行测试。

test这个文件夹中的代码和测试人员无关,而是开发人员自己写的测试代码。项目的第一个测试人员,一定是开发者自己。

import com.bite.mybatisrevisecode.mapper.UserInfoMapper;
import com.bite.mybatisrevisecode.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class MybatisReviseCodeApplicationTests {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Test
    void contextLoads() {
        List<UserInfo> userInfoList = userInfoMapper.selectAllUser();
        System.out.println(userInfoList);
    }
}

测试类上添加了注解@SpringBootTest,给测试类在运行时,就会自动加载Spring的运行环境。我们通过@Autowired这个注解注入我们要测试的类,就可以开始进行测试了。

运行结果如下:

可以看到,只有SQL语句中查询的字段对应的属性才有赋值。

使用IDEA自动生成测试类

在需要测试的Mapper接口中,右键->Generate->Test(也可以使用快捷键 Fn+Alt+Insert ->Test):

选择要测试的代码,点击OK:

测试代码:

import com.bite.mybatisrevisecode.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class UserInfoMapperTest {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Test
    void selectAllUser() {
        List<UserInfo> userInfoList = userInfoMapper.selectAllUser();
        userInfoList.forEach(System.out::println); 
        //这种写法能更清晰地查看列表中每个对象的详细信息
    }
}

记得加@SpringBootTest注解,加载Spring运行环境。

运行结果如下:

三、Mybatis基础操作

1. 打印日志

在Mybatis当中我们可以借助日志查看到SQL语句的执行、执行传递的参数以及执行结果,在配置文件application.yml中进行配置即可。

mybatis:
  configuration:   # 配置打印 Mybatis日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

重新运行程序,可以看到SQL执行语句、传递参数和执行结果。

2. 查(Select)

我们在上面查询时发现,有几个字段是没有赋值的,只有Java对象属性和数据库字段一模一样时,才会进行赋值。

从运行结果上可以看到,我们SQL语句中,查询了delete_flag,create_time,update_time,但是这几个属性却没有赋值。

原因分析:

当自动映射查询结果时,Mybatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写)。这意味着如果发现了ID列和id属性,MyBatis会将列ID的值赋给id属性。

解决方法:

  1. 起别名
  2. 结果映射
  3. 开启驼峰命名
2.1 起别名

在SQL语句中,给列名起别名,保持别名和实体类属性名一样。

@Select("select id, username, password, age, gender, phone, delete_flag as deleteFlag," +
            "create_time as createTime, update_time as updateTime from user_info")
List<UserInfo> selectAllUser2();

SQL语句太长时,使用 “+” 进行字符串拼接。

生成Test后,运行结果如下:

2.2 结果映射
@Select("select id, username, password, age, gender, phone, delete_flag, create_time, " +
            "update_time from user_info")
@Results({
    @Result(column = "delete_flag", property = "deleteFlag"),
    @Result(column = "create_time", property = "createTime"),
    @Result(column = "update_time", property = "updateTime")
})
List<UserInfo> selectAllUser3();

如果其他SQL也希望可以复用这个映射关系,可以给这个Results定义一个名称。

@Select("select id, username, password, age, gender, phone, delete_flag, create_time, " +
        "update_time from user_info")
@Results(id = "resultMap", value = {
        @Result(column = "delete_flag", property = "deleteFlag"),
        @Result(column = "create_time", property = "createTime"),
        @Result(column = "update_time", property = "updateTime")
})
List<UserInfo> selectAllUser3();

@Select("select id, username, password, age, gender, phone, delete_flag, create_time, " +
        "update_time from user_info where id = 1")
@ResultMap(value = "resultMap")
UserInfo selectByResultMap();

生成Test后,运行结果如下:

2.3 开启驼峰命名(推荐)

通常数据库列使用下划线分割各个单词的方法进行命名,而Java属性一般遵循驼峰命名法约定。为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true。

mybatis:
  configuration:
    map-underscore-to-camel-case: true  #配置驼峰⾃动转换

Java代码不做任何处理:

@Select("select * from user_info")
List<UserInfo> selectAllUser();

添加上述配置,运行代码:

3. 参数传递

需求:查找id=4的用户,对应的SQL就是:select * from user_info where id = 4

@Select("select * from user_info where id = 4")
UserInfo selectById();

但如果这样写,只能查找id=4的数据,所以SQL语句中的id值不能写成固定数值,需要变为动态的数值。解决方案:在 selectById 方法中添加一个参数(id),将方法中的参数传递给SQL语句。

使用 #{ } 的方式获取方法中的参数。

@Select("select * from user_info where id = #{id}")
UserInfo selectById(Integer id);

添加测试用例:

@Test
void selectById() {
    System.out.println(userInfoMapper.selectById(4));
}

运行结果:

如果mapper接口方法形参只有一个普通类型的参数,#{ }里面的属性名可以随便写,如:#{id}、#{value}。建议和参数名保持一致。也可以用@Param设置参数别名。

4. #{ } 和 ${ }

Mybatis 参数赋值有两种方式,本文前面使用了 #{ }进行赋值,接下来我们看下二者的区别。

4.1 #{ } 和 ${ } 的使用
4.1.1 Integer类型的参数
@Select("select * from user_info where id = #{id}")
UserInfo selectById(Integer id);

观察打印的日志:

发现我们输出的SQL语句:select * from user_info where id = ?

我们输入的参数并没有在后面拼接,id的值是使用 ?进行占位。这种SQL我们称之为 “预编译SQL”。

我们把 # 改成 $ 再次运行:

@Select("select * from user_info where id = ${id}")
UserInfo selectById(Integer id);

可以看到,这次的参数是直接拼接在SQL中了。

4.2.2 String类型的参数
@Select("select * from user_info where username = #{username}")
UserInfo selectByName(String username);

观察打印的日志,结果正常返回:

我们把 # 改成 $ 再次运行并观察日志:

可以看到,这次的参数依然是直接拼接在SQL语句中了,但是字符串作为参数时,需要添加引号‘’,使用 ${ }不会拼接 ‘’,导致程序报错。

修改代码如下:

@Select("select * from user_info where username = '${username}'")
UserInfo selectByName(String username);

再次运行,结果正确返回:

从上面两个例子可以看出:

#{}使用的是预编译SQL,通过 ?占位的方式,提前对SQL进行编译,然后把参数填充到SQL语句中。#{}会根据参数类型,自动拼接引号 ‘’。

${}会直接进行字符替换,一起对SQL进行编译。如果参数为字符串,需要加上引号 ‘’。(参数为数字类型时,也可以加上,查询结果不变,但是可能会导致索引失效,性能下降)

4.2 #{ } 和 ${ } 的区别

#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别。

4.2.1 性能更高

绝大多数情况下,某一条SQL语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如select的where子句值不同,update的set子句值不同,insert的values值不同)。如果每次都需要经过上面的语法解析,SQL优化、SQL编译等,则效率就明显不行了。

预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同),省去了解析优化等过程,以此来提高效率。

4.2.2 更安全(防止SQL注入)

SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。

由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。

sql注入代码:'or 1='1

先来看看SQL注入的例子:

@Test
void selectByName() {
    System.out.println(userInfoMapper.selectByName("' or 1='1"));
}

注意要把mapper接口对应方法的返回数值类型改成List类型的。

结果依然被正确查询出来了, 其中参数 or 被当做了SQL语句的一部分。

可以看出来,查询的数据并不是自己想要的数据。所以用于查询的字段,尽量使用#{}的查询方式。

5. 增(Insert)

SQL语句:

insert into user_info (username, password, age) values ("user1", "pwd1", "1")

把SQL中的常量替换为动态参数。

Mapper接口:

@Insert("insert into user_info (username, password, age) values (#{username}, #{password}, #{age});")
Integer insertUser(UserInfo userInfo);

直接用UserInfo对象的属性名来获取参数。

测试代码:

@Test
void insertUser() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("user1");
    userInfo.setPassword("pwd1");
    userInfo.setAge(1);
    System.out.println("影响行数: " + userInfoMapper.insertUser(userInfo));
}

运行结果如下:

刷新user_info数据表,可以看出已成功插入数据:

如果设置了 @Param 属性,#{...}需要使用 参数.属性 来获取。

@Insert("insert into user_info (username, password, age) values (#{userinfo.username}, #{userinfo.password}, #{userinfo.age});")
Integer insertUser(@Param("userinfo") UserInfo userInfo);

测试:插入 userinfo(user2,pwd2,2)

观察运行结果:

返回主键

Insert 语句默认返回的是受影响的行数。但有些情况下,数据插入后还需要有后续的关联操作,需要获取到新插入数据的id。

如果想要拿到自增id,需要在Mapper接口的方法上添加一个Options的注解。

@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into user_info (username, password, age) values (#{username}, #{password}, #{age});")
Integer insertUser(UserInfo userInfo);

useGeneratedKeys:这会令使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键,默认值:false。

keyProperty:指定能够唯一识别对象的属性,Mybatis会使用getGeneratedKeys的返回值或insert语句的selectKey子元素设置它的值。 

测试数据:

@Test
void insertUser() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("user3");
    userInfo.setPassword("pwd3");
    userInfo.setAge(3);
    System.out.println("影响行数: " + userInfoMapper.insertUser(userInfo) + ", id: " + userInfo.getId());
}

运行结果:

注意:设置useGeneratedKeys=true之后,方法返回值依然是受影响的行数,自增id会设置在上述keyProperty指定的属性中。

6. 删(Delete)

SQL语句:

delete from user_info where id = 5

把SQL中的常量换成动态参数。

Mapper接口:

@Delete("delete from user_info where id = #{id}")
Integer deleteUserById(Integer id);

测试删除id为5的记录:

7. 改(Update)

SQL语句:

update user_info set phone = "12345" where id = 7;

把SQL中的常量换成动态参数。

Mapper接口:

@Update("update user_info set phone = #{phone} where id = #{id};")
Integer updateUserPhoneById(UserInfo userInfo);

运行结果:

四、Mybatis XML配置文件

Mybatis的开发有两种方式:注解和XML。上文学习了注解的方式,接下来介绍XML的方式。

使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现更复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。

Mybatis XML的方式需要以下两步:

  1. 配置数据库连接字符串和Mybatis
  2. 写持久层代码

1. 配置连接字符串和Mybatis

此步骤需要进行两项设置,数据库连接字符串设置和Mybatis的XML文件配置。

在application.yml文件中配置内容如下:

mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml

2. 写持久层代码

持久层代码分两部分:

  1. 方法定义:Interface
  2. 方法实现:XXX.xml

2.1 添加Mapper接口

数据持久层的接口定义:

import com.bite.mybatisrevisecode.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserInfoXMLMapper {
    List<UserInfo> selectAll();
}
2.2 添加UserInfoXMLMapper.xml

数据持久层的实现,MyBatis的固定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.bite.mybatisrevisecode.mapper.UserInfoXMLMapper">

</mapper>

创建UserInfoXMLMapper.xml,路径参考yml中的配置。

查询所有用户的具体实现:

上述配置完成后,在接口中会有一个报错。按 Alt + Enter 展示解决方案,点击第一个 "Generate statement",xml文件则会自动生成方法框架。

在<select></select>标签中填写查询的SQL:

<select id="selectAll" resultType="com.bite.mybatisrevisecode.model.UserInfo">
    select * from user_info
</select>
  • <mapper>标签:需要指定namespace属性,表示命名空间,值为mapper接口的全限定名,包括全包名.类名。
  • <select>查询标签:是用来执行数据库的查询操作的:
  1. id:是和接口中定义的方法名称一样的,表示对接口的具体实现方法;
  2. resultType:是返回的数据类型,也就是之前定义的实体类userInfo。               

2.3 单元测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class UserInfoXMLMapperTest {

    @Autowired
    private UserInfoXMLMapper userInfoXMLMapper;

    @Test
    void selectAll() {
        userInfoXMLMapper.selectAll().forEach(System.out::println);
    }
}

运行结果:

3. 增删改查操作

查询操作上述已经实现,这里不再赘述。

3.1 增(Insert)

接口:

Integer insertUser(UserInfo userInfo);

实现:

<insert id="insertUser">
    insert into user_info (username, password, age) values (#{username}, #{password}, #{age})
</insert>

@Param设置参数名称,使用方法和注解类似。

返回自增ID

接口定义不变,Mapper.xml实现  设置useGeneratedKeys和keyProperty属性。

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
    insert into user_info (username, password, age) values (#{username}, #{password}, #{age})
</insert>
3.2 删(Delete)

接口:

Integer deleteUserById(Integer id);

实现:

<delete id="deleteUserById">
    delete from user_info where id = #{id}
</delete>
3.3 改(Update)

接口:

Integer updateUserById(UserInfo userInfo);

实现:

<update id="updateUserById">
    update user_info set username = #{username} where id = #{id}
</update>

五、其他查询操作

1. 多表查询

1.1 数据准备

创建文章表:

-- 创建文章表
DROP TABLE IF EXISTS article_info;
CREATE TABLE article_info (
    id INT PRIMARY KEY auto_increment,
    title VARCHAR ( 100 ) NOT NULL,
    content TEXT NOT NULL,
    uid INT NOT NULL,
    delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
    create_time DATETIME DEFAULT now(),
    update_time DATETIME DEFAULT now()
) DEFAULT charset 'utf8mb4';

-- 插入测试数据
INSERT INTO article_info ( title, content, uid ) VALUES ( 'Java', 'Java正文', 1 );

对应实体类:

import lombok.Data;
import java.util.Date;

@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    private Integer uid;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}
1.2 数据查询

需求:根据uid查询作者的名称等相关信息。

SQL:

SELECT
    ta.id,
    ta.title,
    ta.content,
    ta.uid,
    tb.username,
    tb.password
FROM
    article_info ta
        LEFT JOIN user_info tb ON ta.uid = tb.id
WHERE
    ta.id = 1

补充实体类:

import lombok.Data;
import java.util.Date;

@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    private Integer uid;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
    private String username;
    private Integer age;
}

接口定义:

import com.bite.Mybatis_demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ArticleInfoMapper {

    ArticleInfo selectArticleInfoById(Integer id);
}

xml实现:

<select id="selectArticleInfoById" resultType="com.bite.Mybatis_demo.model.ArticleInfo">
    SELECT
        ta.*,
        tb.username,
        tb.age
    FROM article_info ta
        LEFT JOIN user_info tb ON ta.uid = tb.id
    where ta.id=#{id}
</select>

单元测试:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class ArticleInfoMapperTest {

    @Autowired
    private ArticleInfoMapper articleInfoMapper;

    @Test
    void selectArticleInfoById() {
        System.out.println(articleInfoMapper.selectArticleInfoById(1));
    }
}

运行结果:

2. 排序功能

尽管${ }会有SQL注入的风险,但是排序功能需要用到${ }。

使用场景:

Mapper实现:

@Select("select * from user_info order by id ${order}")
List<UserInfo> selectUserInfoByOrder(String order);  
// order传的是正序或逆序(asc/desc)

使用${order}可以实现排序查询,因为此处order参数为String类型,但是SQL语句中,排序规则是不需要加引号的,所以此时的${order}也不加引号。

而使用#{order}不能实现排序查询,因为asc(desc)前后自动加了引号,会导致sql错误。

除此之外,还有表名作为参数时,也只能使用${}。

3. 模糊查询(Like)

like查询使用#{}报错:

@Select("SELECT * FROM `user_info` WHERE username LIKE %#{userName}%'")
List<UserInfo> selectUserInfoByLike(String userName);

把#{}改成${}可以正确查出来,但是${}存在SQL注入的问题,所以不能直接使用${}。

解决办法:使用mysql的内置函数concat()来处理,实现代码如下:

@Select("SELECT * FROM `user_info` WHERE username LIKE CONCAT('%',#{userName},'%')")
List<UserInfo> selectUserInfoByLike(String userName);

六、数据库连接池

数据库连接池是一种管理数据库连接的技术,用于缓存和复用已建立的连接,避免频繁创建和销毁连接带来的性能开销。通过连接池,应用可以高效获取和释放数据库连接,提升系统吞吐量。

没有使用数据库连接池的情况:每次执行SQL语句,要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行完,再关闭连接对象释放资源.这种重复的创建连接,销毁连接比较消耗资源;
使用数据库连接池的情况:程序启动时,会在数据库连接池中创建一定数量的Connection对象,当客户请求数据库连接池,会从数据库连接池中获取Connection对象,然后执行SQL,SQL语句执行完,再把Connection归还给连接池。

优点:1.减少了网络开销 2.资源重用 3.提升了系统性能

常见数据库连接池实现

  1. HikariCP:以高性能著称,轻量级且配置简单,支持 JDBC4(SpringBoot默认使用的数据库连接池)。
  2. Apache DBCP:Apache Commons 项目提供的连接池,稳定性高但性能中等。
  3. C3P0:老牌连接池,功能全面但性能较低,适合传统应用。
  4. Druid:阿里巴巴开源,支持监控和扩展,适合企业级应用。

目前比较流行的是:Hikari、Druid。

七、动态SQL

动态SQL是Mybatis的强大特性之一,能够完成不同条件下不同的sql拼接。官方文档

1. <if>标签

对于必填字段和非必填字段,需要使用<if>动态标签来判断。如gender为非必填字段,具体实现如下:

接口定义:

List<UserInfo> selectByCondition(UserInfo userInfo);

XML实现:

<select id="selectByCondition" resultType="com.bite.mybatisrevisecode.model.UserInfo">
    select * from user_info
    where
        age > #{age}
      <if test="gender!=null">
          and
          gender = #{gender}
      </if>
</select>

或者使用注解方式(不推荐):把上面的SQL(包括标签)使用<script></script>标签括起来就可以。

2. <trim>标签

如果有多个字段是可选项,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式。

标签有如下属性:

  • prefix:表示整个语句块以prefix的值作为前缀
  • suffix:表示整个语句块以suffix的值作为后缀
  • prefixOverrides:表示整个语句块要去除掉的前缀
  • suffixOverrides:表示整个语句块要去除掉的后缀

插入用户信息:xml实现

 <insert id="insertUserByCondition">
        INSERT INTO user_info
        <trim prefixOverrides="," suffixOverrides="," prefix="(" suffix=")">
            <if test="username!=null">
                username,
            </if>
            <if test="password!=null">
                `password`,
            </if>
            <if test="age!=null">
                age,
            </if>
            <if test="gender!=null">
                gender,
            </if>
            <if test="phone!=null">
                phone,
            </if>
        </trim>
        VALUES
        <trim prefixOverrides="," suffixOverrides="," prefix="(" suffix=")">
            <if test="username!=null">
                #{username},
            </if>
            <if test="password!=null">
                #{password},
            </if>
            <if test="age!=null">
                #{age},
            </if>
            <if test="gender!=null">
                #{gender},
            </if>
            <if test="phone!=null">
                #{phone},
            </if>
        </trim>
    </insert>

3. <where>标签

需求:传入的用户对象,根据属性做where条件查询,用户对象中属性不为null的,都为查询条件。

xml实现:

<select id="selectByCondition" resultType="com.bite.mybatisrevisecode.model.UserInfo">
    select id, username, age, gender, phone, delete_flag, create_time, update_time
    from userinfo
    <where>
        <if test="age != null">
            and age = #{age}
        </if>
        <if test="gender != null">
            and gender = #{gender}
        </if>
        <if test="deleteFlag != null">
            and delete_flag = #{deleteFlag}
        </if>
    </where>
</select>

<where>只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR。以上标签也可以使用<trim prefix="where"  prefixOverrides="and">替换,但是此种情况下当子元素都没有内容时,where关键字也会保留。

4. <set>标签

需求:根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容。

<update id="updateUserByCondition">
    update userinfo
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="deleteFlag != null">
            delete_flag = #{deleteFlag},
        </if>
    </set>
    where id = #{id}
</update>

<set>动态的在SQL语句中插入set关键字,并会删掉额外的逗号(用于update语句中)。以上标签也可以使用<trim prefix="set"  suffixOverrides=",">替换。


创作不易 麻烦给个三连支持一下吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mkxxxxxxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值