MyBatis操作数据库(入门)

news2024/11/20 2:39:42

本节目标

  • 使用MyBatis完成简单的增删改查操作,参数传递
  • 掌握MyBatis的两种写法:注解和XML方式
  • 掌握MyBatis相关的日志配置

前言

        在应用分层学习中,我们了解web应用程序一般分为三层,即Controller、Service、Dao。在之前的案例中,请求流程如下:浏览器发起请求,先请求Controller,Controller接收到请求之后,调用Service进行业务逻辑处理,Service再调用Dao,但是Dao层的数据是Mock的,真实的数据应该从数据库中读取。

        我们学习MySQL数据库时,已经学习了JDBC来操作数据库,但是JDBC操作太复杂了。

JDBC操作示例回顾

  1. 创建数据库连接池DataSource
  2. 通过DataSource获取数据库连接Connection
  3. 编写要执行带?占位符的SQL语句
  4. 通过Connection及SQL创建操作命令对象Statement
  5. 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
  6. 使用Stament执行SQL语句
  7. 查询操作:返回结果集ResultSet,更新操作:返回更新的数量
  8. 处理结果集
  9. 释放资源

从上述流程可以看出,对于JDBC来说,整个操作非常的繁琐,那么有没有一种方法,可以更简单、更方便的操作数据库呢?


目录

前言

一、什么是MyBatis?

二、MyBatis入门 

2.1 准备工作

2.1.1 创建工程

2.1.2 数据准备 

2.2 配置数据库连接字符串 

2.3 写持久层代码

三、MyBatis的基本操作 

3.1 打印日志 

3.2 参数传递 

3.3 增(Insert) 

3.4 删(Delete)

3.5 改(update) 

3.6 查(select)

3.6.1 起别名

3.6.2 结果映射 

3.6.3 开启驼峰命名(推荐) 

四、MyBatis XML配置文件 

4.1 配置连接字符串和MyBatis

4.2 写持久层代码 

4.2.1 添加mapper接口

4.2.2 添加UserinfoXmlMapper.xml 

4.3 增删改查操作 

4.3.1 增(Insert)

4.3.2 删(Delete)

4.3.3 改(Update) 

4.3.4 查(Select)

五、多表查询 

六、#{}和${}

6.1 #{}和${}使用

6.2 #{}和${}区别 

6.3 #{}的优势 

6.4 排序功能

6.5 like查询


一、什么是MyBatis?

  • MyBatis是一款优秀的持久层框架,用于简化JDBC的开发
  • MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由apache迁移到google code,并改名为MyBatis。

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

简单来说MyBatis是更简单完成程序和数据库交互的框架,也就是更简单的操作和读取数据工具

二、MyBatis入门 

MyBatis操作数据库的步骤:

  • 准备工作(创建springboot工程、数据库表准备,实体类)
  • 引入MyBatis的相关依赖,配置MyBtis(数据库连接信息)
  • 编写SQL语句(注解/XML)
  • 测试

2.1 准备工作

2.1.1 创建工程

创建springbboot工程,并导入MyBatis的依赖,MySQL的驱动包

2.1.2 数据准备 

创建用户表,并创建对应的实体类UserInfo

-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;

CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;

-- 使用数据数据
USE mybatis_test;

-- 创建表[用户表]
DROP TABLE IF EXISTS userinfo;
CREATE TABLE `userinfo` (
        `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(),
        PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; 

-- 添加用户信息
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.userinfo ( 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;

}

2.2 配置数据库连接字符串 

MyBatis中要连接数据库,需要数据库相关参数配置

  • MySQL驱动类
  • 登录名
  • 密码
  • 数据库连接字符串 

 如果是application.yml文件,配置内容如下:

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

2.3 写持久层代码

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

import com.example.MyBatis.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 userinfo")
    List<UserInfo> getUserInfoAll();
}

MyBatis的持久层接口规范一般都叫xxxMapper

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

  • 程序运行时,框架会自动生成接口的实现类对象(代理对象),并交给Spring的IOC容器管理
  • @Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容

使用IDEA自动生成测试类

1、在需要测试的Mapper接口中,右键->Generate->Test

2、书写测试代码

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 UserInfoMapperTest {
    @Autowired
    private UserInfoMapper userInfoMapper;

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

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

运行结果如下:

三、MyBatis的基本操作 

        上面我们学习了MyBatis的查询操作,接下来我们学习MyBatis的增,删,改操作,在学习这些操作之前,我们先来学习MyBatis日志打印。

3.1 打印日志 

在MyBatis当中我们可以借助日志,查看sql语句的执行、执行传递的参数以及执行结果。

在配置文件中进行配置即可

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

重新运行程序,可以看到SQL执行内容,以及传递参数和执行结果。

3.2 参数传递 

需求:查询id=2的用户,对应的SQL语句就是:select * from userinfo where id = 2

但是这样的话,只能查找id=2的数据,所以SQL语句中的id值不能写成固定的值,需要变为动态的数值。解决办法:在方法中添加一个参数,将方法中的参数,传给SQL语句,使用#{}的方式获取方法中的参数

@Select("select * from userinfo where id = #{id}")
    List<UserInfo> getUserInfoById(Integer id);

注意:如果mapper接口方法形参只有一个普通类型的参数,#{}里面的属性名可以随便写。建议还是和参数名保持一致。

添加测试用例

@Test
    void getUserInfoById() {
        System.out.println(userInfoMapper.getUserInfoById(2));
    }

运行结果:

也可以通过@Param,设置参数的别名,如果使用@Param设置别名,#{}里面的属性名必须和@Param设置一样

@Select("select * from userinfo where id = #{iid}")
    List<UserInfo> getUserInfoById2(@Param("iid") Integer id);

如果#{}里面的属性名必须和@Param设置不一样,我们运行代码。

运行结果:  

3.3 增(Insert) 

Mapper接口

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

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

测试代码:

@Test
    void insert() {
        UserInfo userinfo = new UserInfo();
        userinfo.setUsername("zzz");
        userinfo.setPassword("123");
        userinfo.setAge(18);
        userinfo.setGender(1);
        Integer result = userInfoMapper.insert(userinfo);
        System.out.println("result: " + result + ", id" + userinfo.getId());
    }

运行结果:

返回主键

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

比如订单系统

当我们下完订单之后,需要通知物流系统,这时就需要拿到订单id

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

  • useGeneratedKeys:这会令MyBatis使用JDBC的getGeneratedKeys方法来取出数据库内部生成的主键
  • keyProperty:指定能够唯一识别对象的属性,MyBatis会使用getGeneratedKeys的返回值或insert语句的selectKey子元素设置它的值

3.4 删(Delete)

Mapper接口

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

测试用例

@Test
    void delete() {
        System.out.println(userInfoMapper.delete(10));
    }

运行截图 

3.5 改(update) 

Mapper接口

@Update("update userinfo set username = #{username} where id = #{id}")
    Integer update(UserInfo userInfo);

测试用例

@Test
    void update() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("李四");
        userInfo.setId(9);
        System.out.println(userInfoMapper.update(userInfo));
    }

 运行截图

3.6 查(select)

Mapper接口

@Select("select * from userinfo")
    List<UserInfo> getUserInfoAll();

测试用例

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

运行截图

 观察数据库中的字段

从运行结果上可以看出,我们SQL语句中,查询了delete_flag,create_flag,update_time,这几个字字段在数据库中是有赋值的,但是在控制台中显示为空 ,这是为什么呢

原因分析: 

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

注意: 

MyBatis会根据方法的返回结果进行赋值。

方法用对象Userinfo接收返回结果,MySQL查询出来数据为一条,就会自动赋值给对象。

方法用List<Userinfo>接收返回结果,MySQL查询出来数据为一条或多条时,也会自动赋值给List。

但如果MySQL查询结果返回多条,但是方法返回值使用Userinfo接收,MyBatis执行就会报错。

解决上述为null的办法:

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

3.6.1 起别名

Mapper接口

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

测试代码

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

运行截图

3.6.2 结果映射 

Mapper接口

@Results(id = "resultMap", value = {
            @Result(column = "delete_flag", property = "deleteFlag"),
            @Result(column = "create_time", property = "createTime"),
            @Result(column = "update_time", property = "updateTime")
    })
    @Select("select * from userinfo")
    List<UserInfo> queryAllUser2();

测试代码

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

运行截图

如果其它SQL,也希望可以复用这个映射关系,可以用上述代码定义的名称resultMap

@Select("select * from userinfo")
@ResultMap(value = "resultMap")
List<UserInfo> queryAllUser3();

3.6.3 开启驼峰命名(推荐) 

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

mybatis:
  configuration: 
    map-underscore-to-camel-case: true

四、MyBatis XML配置文件 

MyBatis的开发有两种方式:

  1. 注解
  2. XML

上面学习了注解的方式,接下来我们学习XML的方式

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

MyBatis XML的方式需要以下两步

  • 配置数据库连接字符串和MyBatis
  • 写持久层代码

4.1 配置连接字符串和MyBatis

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

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

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

mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml
  configuration: # 配置打印 MyBatis⽇志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true

配置mybatis xml文件路径,在resources/mapper下创建的所有XML文件 

4.2 写持久层代码 

持久层代码分两部分

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

4.2.1 添加mapper接口

数据库持久层的接口定义:

import com.example.MyBatis.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserinfoXmlMapper {
    List<UserInfo> queryAllUser();
}

4.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.example.MyBatis.mapper.UserinfoXmlMapper">

</mapper>

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

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

<?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.MyBatis.mapper.UserinfoXmlMapper">

    <select id="queryAllUser" resultType="com.example.MyBatis.model.UserInfo">
        select * from userinfo
    </select>
</mapper>

以下是对以上标签的说明:

  • <mapper>标签:需要指定namespace属性,表示命名空间,值为mapper接口的全限定名,包括全包名.类名
  • <select>查询标签:是用来执行数据库的查询操作的:
    • id: 是和Interface(接口)中定义的方法名称一样的,表示对接口的具体实现方法
    • resultType:是返回的数据类型,也就是开头我们定义的实体类

4.3 增删改查操作 

4.3.1 增(Insert)

UserInfoMapper接口:

Integer insertUser(Userinfo userinfo);

UserInfoMapper.xml实现:

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

4.3.2 删(Delete)

UserInfoMapper接口:

Integer deleteUser(Integer id);

UserInfoMapper.xml实现:

<delete id="deleteUser">
        delete from userinfo where id = #{id}
</delete>

4.3.3 改(Update) 

UserInfoMapper接口:

Integer updateUser(Userinfo userinfo);

 UserInfoMapper.xml实现:

<update id="updateUser">
        update userinfo set username = #{username} where id = #{id}
</update>

4.3.4 查(Select)

        同样的,使用XML的方式进行查询,也存在数据封装的问题,我们把SQL语句进行简单修改,查询更多的字段内容。

<select id="queryAllUser" resultType="com.example.mybatis.model.Userinfo">
        select id, username, password, age, gender, phone, delete_flag, create_time, update_time
        from userinfo
</select>

运行截图:

 结果显示:deleteFlag, createTime, updateTime也没有赋值

解决办法和注解类似:

  • 起别名
  • 结果映射
  • 开启驼峰命名

 重点讲下xml来写结果映射

Mapper.xml

<resultMap id="BaseMap" type="com.example.mybatis.model.Userinfo">
        <id column="id" property="id"></id>
        <result column="delete_flag" property="deleteFlag"></result>
        <result column="create_time" property="createTime"></result>
        <result column="update_time" property="updateTime"></result>
    </resultMap>


    <select id="queryAllUser" resultMap="BaseMap" resultType="com.example.mybatis.model.Userinfo">
        select id, username, password, age, gender, phone, delete_flag, create_time, update_time
        from userinfo
    </select>

五、多表查询 

多表查询和单表查询类似,只是SQL不同而已

实体类

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

Mapper接口

import com.example.mybatis.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ArticleInfoXmlMapper {
    ArticleInfo selectArticleAndUserById(Integer id);
}

ArticleInfoXmlMapper.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.mybatis.mapper.ArticleInfoXmlMapper">

    <select id="selectArticleAndUserById" resultType="com.example.mybatis.model.ArticleInfo">
        select article.*, user.username, user.gender from articleinfo article
        left join userinfo user
        on article.uid = user.id
        where article.id = #{id}
    </select>
</mapper>

运行结果截图

 

六、#{}和${}

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

6.1 #{}和${}使用

1、先看Interger类型的参数

@Select("select username, password, age, gender, phone from userinfo where id = #{id}")
    Userinfo queryById4(Integer id);

观察打印日志

 

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

我们把#{}改成${}再观察打印的日志:

@Select("select username, password, age, gender, phone from userinfo where id = ${id}")
    Userinfo queryById5(Integer id);

观察打印日志

 

可以看出,这次参数是直接拼接在SQL语句中了。

2.接下来我们再看String类型的参数

@Select("select username, password, age, gender, phone from userinfo where username = #{name}")
    Userinfo queryByName(String name);

观察打印日志:

 

我们把#{}改成${}再观察打印的日志:

@Select("select username, password, age, gender, phone from userinfo where username = ${name}")
    Userinfo queryByName2(String name);

观察打印日志:

 

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

修改代码如下:

@Select("select username, password, age, gender, phone from userinfo where username = '${name}'")
    Userinfo queryByName2(String name);

再运行观察打印日志:

 

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

  • #{}使用的是预编译SQL,通过?占位的方式,提前对SQL进行编译,然后把参数填充到SQL语句中,#{}会根据参数类型,自动拼接引号''
  • ${}会直接进行字符替换,一起对SQL进行编译,如果参数为字符串,需要加上引号''

注意:参数为数字类型时,也可以加上,查询结果不变,但是可能会导致索引失效,性能下降。

6.2 #{}和${}区别 

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

简单回顾:

当客户发送一条SQL语句给服务器后,大致流程如下:

  1. 解析语法和语义,校验SQL语句是否正确
  2. 优化SQL语句,制定执行计划
  3. 执行并返回结果

一条SQL如果走上述流程处理,我们称之为Immediate Statements(即时SQL)

6.3 #{}的优势 

1、性能更高

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

 

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

2、更安全(防止SQL注入) 

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

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

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

先来看看SQL注入的例子

@Select("select username, password, age, gender, phone from userinfo where username = '${name}'")
    List<Userinfo> queryByName3(String name);

测试代码:

正常访问情况:

@Test
    void queryByName3() {
        System.out.println(userInfoMapper.queryByName3("admin"));
    }

结果运行截图

SQL注入场景

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

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

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

        SQL注入是一种非常常见的数据库攻击手段,SQL注入漏洞也是网络世界中最普遍的漏洞之一。

        如果发生在用户登陆的场景中,密码输入为' or 1 = ' 1,就可能完成登陆(不是一定会发生的场景,需要看登陆代码如何写)

6.4 排序功能

        从上面的例子中,可以得出结论:${}会有SQL注入的风险,所以我们尽量使用#{}完成查询,既然如此,是不是${}就没有存在的必要性呢?

当然不是

接下来我们看下${}的使用场景

Mapper实现

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +
            "from userinfo order by id ${sort}")
    List<Userinfo> queryAllUserBySort(String sort);

使用${sort}可以实现排序查询,而使用#{sort}就不能实现排序查询了。 

        注意:此处sort参数为String类型,但是SQL语句中,排序规则是不需要加引号''的,所以此时的${sort}也不加引号。

我们把${}改成#{}

运行结果:

可以发现,使用#{sort}查询时,desc前后自动给加了引号,导致sql错误

#{}会根据参数类型判断是否拼接引号,如果参数类型为String,就会加上引号。

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

6.5 like查询

like使用#{}报错

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +
            "from userinfo where username like '%#{key}%'")
    List<Userinfo> queryAllUserByLike(String key);

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

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

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +
            "from userinfo where username like concat('%', #{key}, '%'})")
    List<Userinfo> queryAllUserByLike(String key);

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1881871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Nginx反向代理KKFileView遇到问题

使用KKFileView 4.0 以上版本 在KKFileView官网上&#xff0c;关于使用Nginx代理&#xff0c;建议配置如下 一、修改Nacos 在Nginx的conf文件夹中修改 nginx.conf ,新加 红框内的IP地址为代理服务器地址&#xff08;即安装KKFileView的服务器地址&#xff09; 二、修改KKFil…

计算机组成原理——寄存器

文章目录 1. 寄存器 2. 带寄存器的加法器 3. 时钟信号与计算速度 1. 寄存器 上一篇D触发器可以在时钟上沿存储1位数据。如果想存储多个位&#xff08;bit&#xff09;的数据&#xff0c;就需要用多个D触发器并联实现&#xff0c;这种电路称之为寄存器。 寄存器是计算机中央…

MySQL:表的内连接和外连接、索引

文章目录 1.内连接2.外连接2.1 左外连接2.2 右外连接 3.综合练习4.索引4.1见一见索引4.2 硬件理解4.3 MySQL 与磁盘交互基本单位(软件理解)4.4 (MySQL选择的数据结构)索引的理解4.5 聚簇索引 VS 非聚簇索引 5.索引操作5.1 创建索引5.2 查询索引5.3 删除索引 1.内连接 内连接实…

DreamView数据流

DreamView数据流 查看DV中界面启动dag&#xff0c;/apollo/modules/dreamview_plus/conf/hmi_modes/pnc.pb.txt可以看到点击界面的planning按钮&#xff0c;后台其实启动的是/apollo/modules/planning/planning_component/dag/planning.dag和/apollo/modules/external_command…

IT专业入门,高考假期预习指南

七月来临&#xff0c;各省高考分数已揭榜完成。而高考的完结并不意味着学习的结束&#xff0c;而是新旅程的开始。对于有志于踏入IT领域的高考少年们&#xff0c;这个假期是开启探索IT世界的绝佳时机。 一、基础课程预习指南 IT专业是一个广泛的领域&#xff0c;涵盖了从软件开…

Datawhale机器学习day-1

赛题 在当今科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的深度和广度渗透到科研领域&#xff0c;特别是在化学及药物研发中展现出了巨大潜力。精准预测分子性质有助于高效筛选出具有优异性能的候选药物。以PROTACs为例&#xff0c;它是…

服装分销的系统架构

背景 服装的分销规则&#xff1a;组织结构由总公司代理商专卖店构成。总公司全权负责销售业务&#xff0c;并决定给代理商的份额&#xff1b;代理商再给货到专卖店&#xff0c;整个组织机构呈现树状结构&#xff1b;上级机构对下级机构拥有控制权&#xff0c;主要控制其销售的服…

Entity Framework EF Migration 迁移

针对Code First来说关注的只有实体类。当需求变更时只需要添加新的实体类或者在实体类中添加、删除、修改属性即可。但是修改完成之后要如何将修改同步到数据库中&#xff1f; migration 机制就出现了 ●启用Migrations   ●通过Add-Migration添加Migration   ●Update-D…

WPF/C#:BusinessLayerValidation

BusinessLayerValidation介绍 BusinessLayerValidation&#xff0c;即业务层验证&#xff0c;是指在软件应用程序的业务逻辑层&#xff08;Business Layer&#xff09;中执行的验证过程。业务逻辑层是应用程序架构中的一个关键部分&#xff0c;负责处理与业务规则和逻辑相关的…

初中英语优秀作文分析-006How to Deal with the Exam Stress-如何应对考试压力

更多资源请关注纽扣编程微信公众号 记忆树 1 We students are very busy with schoolwork and in the face of many exams every school day. 翻译 我们学生忙于功课&#xff0c;每个上学日都面临许多考试。 简化记忆 考试 句子结构 We students 主语 我们学生&#xf…

手把手教你入门vue+springboot开发(六)--后端代码解读与优化

文章目录 前言一、Lombok库二、spring-boot-starter-validation库三、ThreadLocalUtil四、全局异常处理总结 前言 前面我们已经把vuespringboot前后端分离开发和打包部署过程全部打通了&#xff0c;通过一个简单的demo来演示整个过程&#xff0c;主要关注在开发工具使用、框架…

CAD使用技巧,图片去边框,直线等分

CAD插入图片之后怎么去除图片边框 有时候我们需要将图片插入到CAD里面&#xff0c;但是发现插入进去之后&#xff0c;图片周围带有白色边框&#xff0c;这样看着就不是很舒服&#xff0c;要去除边框要如何操作呢 命令操作法&#xff1a;在命令栏输入“imageframe”回车&#xf…

蜜雪冰城小程序逆向

app和小程序算法一样 小程序是wasm

谈谈Flink消费kafka的偏移量

offset配置: flinkKafkaConsumer.setStartFromEarliest():从topic的最早offset位置开始处理数据&#xff0c;如果kafka中保存有消费者组的消费位置将被忽略。 flinkKafkaConsumer.setStartFromLatest():从topic的最新offset位置开始处理数据&#xff0c;如果kafka中保存有消费…

Golang | Leetcode Golang题解之第200题岛屿数量

题目&#xff1a; 题解&#xff1a; func numIslands(grid [][]byte) int {res : 0for i : 0; i < len(grid); i {for j : 0; j < len(grid[i]); j {if grid[i][j] 1 {resdfs(grid, i, j)}}}return res }func dfs(grid [][]byte, r, c int) {h, w : len(grid), len(gri…

C++感受12-Hello Object 派生版

不变的功能&#xff0c;希望直接复用原有代码&#xff1b;变化的功能&#xff0c;希望在分开的代码里实现。 派生的基本概念和目的如何定义派生类以及创建派生对象派生对象的生死过程 0. 课堂视频 ff14-HelloObject-派生版 1. 派生的基本概念与目的 编程&#xff0c;或者说软…

Games101学习笔记 Lecture 14: Ray Tracing 2 (Acceleration Radiometry)

Lecture 14: Ray Tracing 2 (Acceleration & Radiometry 一、加速光线追踪 AABB1.均匀网格 Uniform Spatial Partitions (Grids)①前处理-构建加速网格②射线与场景相交③网格分辨率④适用情况 2.空间划分KD-Tree①预处理②数据结构③遍历④问题 3.对象划分 & 包围盒层…

使用Python绘制极坐标图

使用Python绘制极坐标图 极坐标图极坐标图的优点使用场景 效果代码 极坐标图 极坐标图&#xff08;Polar Chart&#xff09;是一种图表类型&#xff0c;用于显示在极坐标系中的数据。极坐标图使用圆形坐标系&#xff0c;角度表示一个变量的值&#xff0c;半径表示另一个变量的…

森马基于MaxCompute+Hologres+DataWorks构建数据中台

讲师&#xff1a;晋银龙 浙江森马数仓高级经理 本次案例主要分享森马集团面对多年自建的多套数仓产品体系&#xff0c;通过阿里云MaxComputeHologresDataWorks统一数仓平台&#xff0c;保障数据生产稳定性与数据质量&#xff0c;减少ETL链路及计算时间&#xff0c;每年数仓整体…