[Spring] MyBatis操作数据库(基础)

news2024/9/28 9:30:46

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

目录

  • 1.什么是MyBatis
  • 2. MyBatis入门
    • 2.1 创建项目引入依赖
    • 2.2 准备数据与相关实体类
    • 2.3 配置数据库相关信息
    • 2.4 编写Mapper
    • 2.5 编写测试代码
  • 2. MyBatis基础操作
    • 2.1 MyBatis打印日志
    • 2.2 参数传递
    • 2.3 增(insert)
    • 2.4 删(Delete)
    • 2.5 改(Update)
    • 2.6 查(select)
      • 3.6.1 起别名
      • 3.6.2 结果映射
      • 2.6.3 开启驼峰命名与蛇形命名转换(推荐做法)
  • 3. MyBatis XML配置文件
    • 3.1 配置连接字符串和MyBatis
    • 3.2 持久层代码
      • 3.2.1 添加mapper接口
      • 3.2.2 添加UserInfoXMLMapper.xml
      • 3.2.3 单元测试
    • 3.3 增删改查操作
      • 3.3.1 增(Insert)
      • 3.3.2 删(Delete)
      • 3.3.3 改(Update)
      • 3.3.4 查(select)
  • 4. 其他查询操作
    • 4.1 多表查询(联合查询)
      • 4.1.1 准备数据,创建对应的实体类
      • 4.1.2 查询数据
    • 4.2 #{} 和 ${}
      • 4.2.1 使用
      • 4.2.2 #{} 和 ${}的区别(高频面试题)
    • 4.3 排序功能
    • 4.4 like查询

1.什么是MyBatis

在前面,我们学习过JDBC编程对数据库的操作,不过JDBC对于数据库的操作在步骤上过于麻烦,所以我们在Spring中引入了MyBatis.

  • MyBatis是一款优秀的持久层框架,用于简化JDBC的开发.
    那么持久层是什么意思呢?持久层指的就是持久化操作的层,通常指的是数据库的访问层,通常用来操作数据库.
    在这里插入图片描述
    简单来说MyBatis是更简单完成程序和数据库交互的框架,也就是更简单的操作和读取数据库工具.

2. MyBatis入门

MyBatis操作数据库的步骤:

  1. 创建项目,引入依赖
  2. 准备数据与实体类
  3. 配置数据库相关信息,包括数据库URL,账号和密码
  4. 编写Mapper(持久层代码),包括接口interface与sql语句.
  5. 生成测试代码,对编写的接口进行测试.

2.1 创建项目引入依赖

  • 创建SpringBoot工程,并导入MyBatis的起步依赖和MySQL的驱动包.把MyBatis Framework和MySQL driver这两个选项都勾选上.
    在这里插入图片描述
  • 创建项目之后,我们就可以发现,项目的pom文件中自动导入了两个选项的依赖项.
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter-test</artifactId>
    <version>3.0.3</version>
    <scope>test</scope>
</dependency>

如果我们在创建工程的时候忘记勾选了这两个选项,也可以通过右键点击生成,选择edit starter自动创建依赖.
在这里插入图片描述

2.2 准备数据与相关实体类

  • 创建用户表,并创建对应的实体类User.
-- 创建数据库 
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,其中实体类的属性名和表中的字段名一一对应.
package com.jrj.mybatis;

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.3 配置数据库相关信息

MyBatis中要连接数据库需要数据库相关参数的配置,包括数据库URL,用户名,密码,MySQL驱动类.这些配置信息均在配置文件中完成.用于连接对应的数据库.下面我们以yml文件为例:

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

其中,mybatis_test就对应的是数据库的名字.
[注意] 如果password是纯数字的话,需要在数字的的外围用单引号括起来.

2.4 编写Mapper

package com.jrj.mybatis.mapper;

import com.jrj.mybatis.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")
    public List<UserInfo> selectAllUser();
}

@Mapper注解: 表示的是MyBatis中的Mapper接口,在程序运行的时候,这个接口会交给IoC容器管理.
@Select注解: 表示的是sql语句是一个select查询语句,也就是注解方法具体实现的内容.
需要注意的一点是,像这样返回多条数据的接口,一定要用List来接收.

2.5 编写测试代码

如何生成测试代码:

  • 在想要测试的接口中点击右键,点击生成.
    在这里插入图片描述
  • 点击测试
    在这里插入图片描述
  • 选择想要测试的方法
    在这里插入图片描述
  • 测试类会在src目录中的test目录下自动生成.
    在这里插入图片描述
  • 之后在对应的方法中编写测试代码.
@SpringBootTest
class UserInfoMapperTest {
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Test
    void selectAllUser() {
        List<UserInfo> list = userInfoMapper.selectAllUser();
        for (UserInfo userInfo:list){
            System.out.println(userInfo);
        }
    }
}
  1. @SpringBootTest注解:会在测试类运行的时候,自动加载Spring环境,
  2. @Autowired进行DI注入之后,我们便可以使用对应的类调用对应的方法进行测试.
  3. @Test注解表示该方法是测试方法, 加上该注解后会在运行测试方法的时候在控制台上显示测试方法的运行结果.

2. MyBatis基础操作

2.1 MyBatis打印日志

在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果,在配置文件中进行配置即可.下面是yml配置文件的格式:

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

重新运行程序,我们可以看到sql语句的内容,以及传递的参数类型和执行结果.

在这里插入图片描述

2.2 参数传递

现在我们需要查找id=4的用户,对应的sql是select * from userinfo where id=4.

@Select("select * form userinfo where id = 4")
public UserInfo selectUser();

但是这样的话,就只能查询到id为4用户的数据,所以我们不建议把sql语句这样写死.需要变为动态的数值.
解决办法就是在selectUser方法中添加一个参数(id),将方法中的参数传递给sql语句.使用#{}的方式在sql语句中获取方法中的参数.

@Select("select * from userinfo where id = #{id}")
public UserInfo selectUser(Integer id);
void selectUser() {
    System.out.println(userInfoMapper.selectUser(4));
}

运行测试代码:
在这里插入图片描述
如果mapper接口方法中的形式参数只有一个,#{}里面的属性名可以随便写.因为参数和属性名都只有一个,只能是一一对应的关系.如果参数有多个,属性名和参数名就必须保持一致,或者使用param1,param2…按照顺序对应.

  • 按照名字对应
@Select("select * from userinfo where username = #{name} and id = #{id}")
public UserInfo selectUser2(String name,Integer id);
@Test
void selectUser2() {
    System.out.println(userInfoMapper.selectUser2("admin",1));
}

测试结果:
在这里插入图片描述

  • 按照参数顺序对应
    @Select("select * from userinfo where username = #{param1} and id = #{param2}")
    public UserInfo selectUser3(String name,Integer id);
    @Test
    void selectUser3() {
        System.out.println(userInfoMapper.selectUser3("admin",1));
    }

测试结果:
在这里插入图片描述

也可以通过@Param注解,设置参数的别名,如果使用@Param设置别名,注解中的别名必须和sql中的属性名保持一致.

@Select("select * from userinfo where id = #{ID}")
public UserInfo selectUser4(@Param("ID") Integer id);
@Test
void selectUser4() {
    System.out.println(userInfoMapper.selectUser4(1));
}

运行结果:
在这里插入图片描述

2.3 增(insert)

sql语句:
insert into userinfo (username, `password`, age, gender, phone) values ("zhaoliu","zhaoliu",19,1,"18700001234")
把sql中的常量换为动态的参数.
Mapper接口:

@Insert("insert into userinfo (id,username,password,age,gender,phone) values (" +
        "#{id},#{username},#{password},#{age},#{gender},#{phone})")
public Integer insertUser1(UserInfo userInfo);

这里我们可以直接使用UserInfo对象的属性来获取参数.由于Insert返回的是改变数据库的行数,所以接口的返回值是Integer.

@Test
void insertUser1() {
    UserInfo userInfo = new UserInfo();
    userInfo.setId(5);
    userInfo.setUsername("wangwu");
    userInfo.setPassword("12345");
    userInfo.setAge(20);
    userInfo.setGender(1);
    userInfo.setPhone("13334768907");
    userInfoMapper.insertUser1(userInfo);
}

运行结果:
在这里插入图片描述
查看数据库:id为5的数据插入成功.
在这里插入图片描述
接下来我们来设置@Param属性,那么#{}就需要用参数.属性的方式来获取.

  • 返回主键
    Insert语句默认返回的是受影响的行数,但是有些情况下,数据插入之后,还需要有后续的关联操作,需要获取到新插入的数据的id.如果想要拿到自增id,需要在Mapper接口的方法上添加⼀个Options的注解.
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into userinfo (id,username,password,age,gender,phone) values (" +
        "#{id},#{username},#{password},#{age},#{gender},#{phone})")
public Integer insertUser2(UserInfo userInfo);

其中useGeneratedKeys表示的是是自增?keyProperty表示的是哪个字段自增?

@Test
void insertUser2() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("zhaoliu");
    userInfo.setPassword("1234566");
    userInfo.setAge(21);
    userInfo.setGender(0);
    userInfo.setPhone("13334098738");
    System.out.println("change col " + userInfoMapper.insertUser2(userInfo) + "id = " + userInfo.getId());
}

测试运行:
在这里插入图片描述
我们看到影响的行数是1,自增的id为7.下面我们观察数据库:
在这里插入图片描述

2.4 删(Delete)

sql语句:
delete from userinfo where id=6.
把sql语句中的常量替换为动态参数.

@Delete("delete from userinfo where id = #{id}")
public Integer deleteUser1(Integer id);
@Test
void deleteUser1() {
    System.out.println(userInfoMapper.deleteUser1(6));
}

查看数据库,我们看到id=6的数据被删除掉了.
在这里插入图片描述

2.5 改(Update)

sql语句:
update userinfo set username="zhaoliu" where id=5
把SQL中的常量替换为动态的参数
Mapper接口:

@Update("update userinfo set username = #{username} where id = #{id}")
public Integer updateUser(UserInfo userInfo);
    @Test
    void updateUser() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(5);
        userInfo.setUsername("tianqi");
        System.out.println(userInfoMapper.updateUser(userInfo));
    }

测试运行:我们看到数据库中id为5的名字被改成了tianqi.
在这里插入图片描述

2.6 查(select)

我们在上面查询的时候,发现有几个字段是没有赋值的.只有Java对象属性和数据库字段⼀模⼀样时,才会进行赋值.
在这里插入图片描述
在我们查询所有数据的时候,我们发现,对象的创建时间,更新时间,删除逻辑数字都是null.
原因:
当自动映射查询结果的时候,MyBatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写) .这意味着如果发现了ID列和id属性,MyBatis会将列ID的值赋给id属性.但是我们的创建时间,更新时间,删除逻辑数字,在数据库中是蛇形结构的名字,而在Java类中是小驼峰的格式.
在这里插入图片描述
下面是解决办法:

3.6.1 起别名

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

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

当sql语句太长的时候,我们可以使用+进行拼接.

3.6.2 结果映射

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

@Results注解中可以用大括号括起多个@Result映射,@Result前面的参数是表的字段名,后面是Java类的属性.也就是字段与属性的映射关系.

@Test
void selectAllUser3() {
    List<UserInfo> list = userInfoMapper.selectAllUser3();
    for (UserInfo userInfo:list){
        System.out.println(userInfo);
    }
}

运行测试:
在这里插入图片描述
我们看到,后面的几个属性被显示了出来.
如果其他sql也想复用这一组映射,可以给这一组@Results映射自定义一个名称. 之后在想要复用这个sql映射的地方使用@ResultMap(映射名称)来复用映射.

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

 @ResultMap(value = "resultMap1")
 @Select("select * from userinfo where id = #{id}")
 public UserInfo selectUser5(Integer id);

@ResultMap注解中的value的值和上面映射的id名字一样.方可复用.

2.6.3 开启驼峰命名与蛇形命名转换(推荐做法)

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

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

转换规则:abc_xyz=>abcXyz蛇形转换为小驼峰格式.
Java代码不做任何处理:

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

我们看到原来没有赋值上的属性被赋值了.
在这里插入图片描述

mysql表的设计规范:
自增id,创建日期,更新日期,这几个字段即使业务没有需求,也必须有.

3. MyBatis XML配置文件

MyBatis的开发方式从大方向上分为两种,除了我们上面展示的使用注解的方式,我们还可以通过xml配置文件来开发.

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

这种开发方式大致分为两步:

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

3.1 配置连接字符串和MyBatis

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

# 数据库连接配置 
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?
    characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件 
mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml

mapper-locations中的value值,classpath表示的是项目中的resource目录.mapper表示的是一个自定义目录.一般我们使用xml操作数据库的代码都会单独放在一个mapper目录中,之后**Mapper.xml表示的是以这个结尾的xml文件就是操作数据库的xml文件.

3.2 持久层代码

持久层代码分为两部分:

  1. 方法定义: Interface
  2. 方法实现: xxx.xml
    在这里插入图片描述

3.2.1 添加mapper接口

package com.jrj.mybatis.mapper;

import com.jrj.mybatis.UserInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserInfoXMLMapper {
    public List<UserInfo> selectAllUser1();
}

3.2.2 添加UserInfoXMLMapper.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.jrj.mybatis.mapper.UserInfoXMLMapper">
    
</mapper>

mapper标签中加的是带有@Mapper注解接口的路径,即想要通过MyBatis操作数据库的Mapper接口.
查询所有用户的具体实现:

<?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.jrj.mybatis.mapper.UserInfoXMLMapper">
    <select id="selectAllUser1" resultType="com.jrj.mybatis.UserInfo">
        select * from userinfo
    </select>
</mapper>

其中select标签中,id="selectAllUser1"代表的是Mapper中的方法名. resultType="com.jrj.mybatis.UserInfo"代表的是sql查询之后返回的类型,也就是我们开头定义的实体类,只有select类型的语句会有返回值的类型.注意,是sql查询之后返回的类型,不是接口返回值的类型,sql查询之后返回的是UserInfo类,而接口返回的是List类型.
标签中间写的是sql语句.
我们可以安装一个插件,叫做MyBatisX,这个插件可以自动帮助我们生成xml标签.我们是需要写sql语句即可.

注意,在我们的方法名中如果有select,Insert这样的关键字的时候,插件会自动根据名字自动生成标签类型.如果没有,需要在生成的时候进行选择.
在这里插入图片描述

3.2.3 单元测试

测试代码如下:

@Test
void selectAllUser1() {
     List<UserInfo> list = userInfoXMLMapper.selectAllUser1();
     for (UserInfo userInfo: list){
         System.out.println(userInfo);
     }
 }

测试结果:
在这里插入图片描述

3.3 增删改查操作

3.3.1 增(Insert)

UserInfoMapper接口:

public Integer insertUser(UserInfo userInfo);

xml:

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

测试代码:

@Test
void insertUser() {
    UserInfo userInfo = new UserInfo();
    userInfo.setId(8);
    userInfo.setUsername("zhubajie");
    userInfo.setAge(22);
    userInfo.setPassword("6666666");
    userInfo.setGender(0);
    userInfo.setPhone("487362849326");
    userInfoXMLMapper.insertUser(userInfo);
}

运行结果:
在这里插入图片描述
在这里插入图片描述
与注解实现类似,要是在形参的前面加上@Param注解的话,在sql语句中的#{}就必须使用对象名.属性名来访问.
Mapper接口:

public Integer insertUser(@Param("userinfo") UserInfo userInfo);

xml:

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

3.3.2 删(Delete)

3.3.3 改(Update)

删和改和上面的增是相同的道理,这里我们不再赘述.

3.3.4 查(select)

同样,使用xml进行查询的时候,同样也存在sql字段名和类属性对应不上的问题.
xml:

<select id="selectAllUser1" resultType="com.jrj.mybatis.UserInfo">
    select * from userinfo
</select>

Mapper接口:

public List<UserInfo> selectAllUser1();

运行结果:
在这里插入图片描述
我们看到一些字段名仍然没有被赋值.
解决办法与注解类似:

  1. 起别名
  2. 结果映射
  3. 开启驼峰与蛇形转换.
    1,3 与注解一样,不再赘述.下面我们来看一下结果映射法如何解决字段与属性映射以及主键问题.
    xml:
<resultMap id="Map" type="com.jrj.mybatis.UserInfo">
    <id column="id" property="id"></id>
    <result column="delete_flag" property="deleteFlag"></result>
    <result column="create_time" property="createTime"></result>
    <result column="update" property="updateTime"></result>
</resultMap>
<select id="selectAllUser1" resultType="com.jrj.mybatis.UserInfo" resultMap="Map">
    select * from userinfo
</select>

解释:
在这里插入图片描述

4. 其他查询操作

4.1 多表查询(联合查询)

多表查询和单表查询类似,只不过就是sql不同而已,具体sql可参考:
https://lilesily12385.blog.csdn.net/article/details/137519405

4.1.1 准备数据,创建对应的实体类

上面建了⼀张用户表,我们再来建⼀张文章表,进行多表关联查询.
文章表的uid,对应用户表的id.

  • 准备数据
-- 创建⽂章表 
DROP TABLE IF EXISTS articleinfo;
CREATE TABLE articleinfo (
 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 articleinfo ( title, content, uid ) VALUES ( 'Java', 'Java正⽂', 1 
);
  • 创建对应model
package com.jrj.mybatis;

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

4.1.2 查询数据

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

SELECT
 ta.id,
 ta.title,
 ta.content,
 ta.uid,
 tb.username,
 tb.age,
 tb.gender 
FROM
 articleinfo ta
 LEFT JOIN userinfo tb ON ta.uid = tb.id 
WHERE
 ta.id =1

补充实体类:

@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;
    private Integer gender;
}

接口定义:

@Mapper
public interface ArticleInfoMapper {
    @Select("select " +
"articleinfo.id,articleinfo.title,articleinfo.content,articleinfo.uid,articleinfo.delete_flag,articleinfo.create_time,articleinfo.update_time,userinfo.username,userinfo.age,userinfo.gender " +
            "from " +
            "articleinfo left join userinfo " +
            "on articleinfo.uid = userinfo.id " +
            "where userinfo.id = #{id}")
    public ArticleInfo selectArticle(Integer id);
}

测试代码:

@SpringBootTest
class ArticleInfoMapperTest {
    @Autowired
    public ArticleInfoMapper articleInfoMapper;
    @Test
    void selectArticle() {
        System.out.println(articleInfoMapper.selectArticle(1));
    }
}

运行结果:
在这里插入图片描述

一般情况下,我们不会轻易使用联合查询,因为它是一种慢查询,会影响集群的性能.我们一般会把联合查询的sql拆分成多个单表查询.
当然有些情况下可以使用多表查询,这些情况对于性能要求都比较小:比如内部员工使用的系统,B端项目(平台的商家端).

4.2 #{} 和 ${}

在MyBatis中,参数赋值的方法不仅仅只有#{},而且还有${},下面我们来看一下二者的区别.

4.2.1 使用

  1. 先看Integer类型的参数
@Select("select * from userinfo where id = #{id}")
public UserInfo selectUser(Integer id);

观察日志:
在这里插入图片描述
我们发现,通过次接口输出的sql语句是:
select * form userinfo where id = ?
我们输⼊的参数并没有在后面拼接,id的值是使用? 进行占位.这种SQL我们称之为"预编译SQL".预编译sql我们在mysql中的JDBC编程中有提到过.就是把参数的值直接赋给了前面的id.
#{}替换为${}之后再次观察打印日志:

@Select("select * from userinfo where id = ${id}")
public UserInfo selectUser(Integer id);

在这里插入图片描述
我们看到,参数直接拼接到了sql语句中.
2. 接下来就是String类型的参数

@Select("select * from userinfo where username = #{name}")
public UserInfo selectUser6(String name);

观察打印日志:
在这里插入图片描述
#{}换为${}再观察打印日志.

@Select("select * from userinfo where username = ${name}")
public UserInfo selectUser6(String name);

观察日志:
在这里插入图片描述
我们发现,这次的结果依然是把参数直接拼接在了sql中,但是字符串作为参数的时候需要添加引号 ‘’ ,使
${} 不会拼接引号
‘’ ,导致程序报错.
我们修改代码:

@Select("select * from userinfo where username = '${name}'")
public UserInfo selectUser6(String name);

再次运行,正常返回:
在这里插入图片描述
从上面两个例子可以总结:
#{} 使用的是预编译SQL,通过?占位的方式,提前对SQL进行编译,然后把参数赋值到SQL语句中.#{} 会根据参数类型,自动拼接引号’’ .${} 会直接进行字符替换,直接把参数拼接到sql中,⼀起对SQL进行编译.如果参数为字符串,需要加上引号 ‘’ .

4.2.2 #{} 和 ${}的区别(高频面试题)

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

  1. #{}相对于${}性能更高.
    预编译SQL,编译⼀次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输⼊的参数不同),省去了解析优化等过程,以此来提高效率.
  2. #{}更安全(防止sql注入)
    SQL注⼊:是通过操作输⼊的数据来修改事先定义好的SQL语句的本意,以达到执行代码对服务器进行攻击的方法.由于没有对用户输入进行充分检查,而SQL⼜是拼接而成,在用户输⼊参数时,在参数中添加⼀些SQL关键字,达到改变SQL运⾏结果的目的,也可以完成恶意攻击.
    我们下面来举一个sql注入的例子:sql注入代码: ’ or 1='1
@Select("select * from userinfo where username = '${name}'")
public UserInfo selectUser6(String name);

sql注入后:

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

运行结果:
在这里插入图片描述
sql就返回所有用户的信息.并不是我们想要的信息.所以我们尽量使用#{}的方式.

4.3 排序功能

即使${}会有sql注入的风险.有些情况下,${}还是有他自己的作用.比如对数据进行排序.
Mapper实现:把数据根据id降序排名.

    @Select("select * from userinfo order by id ${sort}")
    public List<UserInfo> selectUser7(String sort);
@Test
void selectUser7() {
    System.out.println(userInfoMapper.selectUser7("desc"));
}

返回了正确的结果;
在这里插入图片描述
==如果把KaTeX parse error: Expected 'EOF', got '#' at position 3: 改成#̲,就会自动对字符串加上``' …{}``.

4.4 like查询

like使用#{}会报错.会给字符串加上单引号.

@Select("select * from userinfo where username like '%#{name}%'")
public UserInfo selectUser8(String name);
@Test
void selectUser8() {
    System.out.println(userInfoMapper.selectUser8("dmi"));
}

在这里插入图片描述
虽然使用${}会成功,但是存在sql注入问题,不可以直接使用.所以我们通过sql中的一个函数concat()来处理,它是一个字符串拼接函数.

@Select("select * from userinfo where username like concat('%',#{name},'%')")
public UserInfo selectUser8(String name);

运行成功:
在这里插入图片描述

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

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

相关文章

Elasticsearch概念及ELK安装

1、Elasticsearch是什么 它是elastic技术栈中的一部分。完整的技术栈包括&#xff1a; Elasticsearch&#xff1a;用于数据存储、计算和搜索 Logstash/Beats&#xff1a;用于数据收集 Kibana&#xff1a;用于数据可视化 整套技术栈被称为ELK&#xff0c;经常用来做日志收集…

WPF启动失败报System.Windows.Automation.Peers.AutomationPeer.Initialize()错误解决

问题描述 win10系统上WPF程序启动后就崩溃&#xff0c;通过查看崩溃日志如下&#xff1a; 应用程序: xxx.exe Framework 版本: v4.0.30319 说明: 由于未经处理的异常&#xff0c;进程终止。 异常信息: System.TypeLoadException 在 System.Windows.Automation.Peers.Automatio…

CMake 使用 OpenCV:从库中查找包含头文件

前言 在开发使用 OpenCV 的项目时&#xff0c;正确配置 CMake 是确保项目顺利构建和运行的关键。开发过程经常存在各种各样的意外和偶然, 是困难也是收获. 比如一直好好的项目, include某个头文件, 编译突然出现:No such file or directory CmakeTest/test_opencv.h:4: error:…

一套成熟的实验室信息管理系统源码,.Net 检验系统LIS源码,实现从采集、检测、报告、归档的全程跟踪管理

一套成熟的实验室信息管理系统源码。在长期的医疗信息化实践中&#xff0c;我们分析总结了大量客户实例&#xff0c;建立了以病人为中心、以业务处理为基础、以提高检验科室管理水平和工作效率为目标的产品开发思路&#xff0c;将医学检验、科室管理和财务统计等检验科室/实验室…

ControlNet on Stable Diffusion

ControlNet on Stable Diffusion 笔记来源&#xff1a; 1.Adding Conditional Control to Text-to-Image Diffusion Models 2.How to Use OpenPose & ControlNet in Stable Diffusion 3.ControlNet与DreamBooth&#xff1a;生成模型的精细控制与主体保持 4.Introduction t…

【Python实战】Google Chrome的离线小恐龙游戏

文章目录 Google Chrome的离线小恐龙游戏项目结构大纲 &#x1f4ca;&#x1f463;逐步编码过程 &#x1f9e9;&#x1f4a1;第一步&#xff1a;项目初始化与主程序框架第二步&#xff1a;实现T-Rex的跳跃功能第三步&#xff1a;添加障碍物和碰撞检测第四步&#xff1a;添加得分…

Python3网络爬虫开发实战(1)爬虫基础

一、URL 基础 URL也就是网络资源地址&#xff0c;其满足如下格式规范 scheme://[username:password]hostname[:port][/path][;parameters][?query][#fragment] scheme&#xff1a;协议&#xff0c;常用的协议有 Http&#xff0c;https&#xff0c;ftp等等&#xff1b;usern…

正点原子 通用外设配置模型 GPIO配置步骤 NVIC配置

1. 这个是通用外设驱动模式配置 除了初始化是必须的 其他不是必须的 2. gpio配置步骤 1.使能时钟是相当于开电 2.设置工作模式是配置是输出还是输入 是上拉输入还是下拉输入还是浮空 是高速度还是低速度这些 3 和 4小点就是读写io口的状态了 3. 这个图是正点原子 将GPIO 的时…

2024中国大学生算法设计超级联赛(2)

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;彩笔ACMer一枚。 &#x1f3c0;所属专栏&#xff1a;杭电多校集训 本文用于记录回顾总结解题思路便于加深理解。 &#x1f4e2;&#x1f4e2;&#x1f4e2;传送门 A - 鸡爪解题思…

eclipse修改tomcat的Jre运行环境

1.双击tomcat 2.RuntimeEnvironment 3.选择

轨道式智能巡检机器人,助力综合管廊安全运维

1 引言 当前城市综合管廊建设已经成为世界范围内的发展趋势&#xff0c;2017年5月住建部、发改委联合发布《全国城市市政基础设施建设“十三五”规划》&#xff0c;截至2017年4月底国内地下综合管廊试点项目已开工建设687 km&#xff0c;建成廊体260 km&#xff0c;完成投资40…

redis的使用场景-热点数据缓存

1.什么是缓存&#xff1f; 把一些经常访问的数据放入缓存中&#xff0c;减少访问数据库的频率&#xff0c;减少数据库的压力&#xff0c;从而提高程序的性能。【内存中存储】 2.缓存的原理 通过上图可以看出程序首先访问缓存&#xff0c;如果缓存中有访问的数据会直接方会给客…

分布式系统常见软件架构模式

常见的分布式软件架构 Peer-to-Peer (P2P) PatternAPI Gateway PatternPub-Sub (Publish-Subscribe)Request-Response PatternEvent Sourcing PatternETL (Extract, Transform, Load) PatternBatching PatternStreaming Processing PatternOrchestration Pattern总结 先上个图&…

基于Golang+Vue3快速搭建的博客系统

WANLI 博客系统 项目介绍 基于vue3和gin框架开发的前后端分离个人博客系统&#xff0c;包含md格式的文本编辑展示&#xff0c;点赞评论收藏&#xff0c;新闻热点&#xff0c;匿名聊天室&#xff0c;文章搜索等功能。 项目在线访问&#xff1a;http://bloggo.chat/ 或 http:/…

Photos框架 - 自定义媒体资源选择器(数据部分)

引言 在iOS开发中&#xff0c;系统已经为我们提供了多种便捷的媒体资源选择方式&#xff0c;如UIImagePickerController和PHPickerViewController。这些方式不仅使用方便、界面友好&#xff0c;而且我们完全不需要担心性能和稳定性问题&#xff0c;因为它们是由系统提供的&…

基于扩散的生成模型的语音增强和去噪

第二章 目标说话人提取之《Speech Enhancement and Dereverberation with Diffusion-based Generative Models》 文章目录 前言一、任务二、动机三、挑战四、方法1.方法:基于分数的语音增强生成模型(sgmse)2.网络结构 五、实验评价1.数据集2.采样器设置和评价指标3.基线模型4.评…

godot新建项目及设置外部编辑器为vscode

一、新建项目 初次打开界面如下所示&#xff0c;点击取消按钮先关闭掉默认弹出的框 点击①新建弹出中间的弹窗②中填入项目的名称 ③中设置项目的存储路径&#xff0c;点击箭头所指浏览按钮&#xff0c;会弹出如下所示窗口 根据图中所示可以选择或新建自己的游戏存储路径&…

音视频开发之旅(85)- 图像分类-VGG模型解析

目录 1. VGG解决的问题 2. 网络结构和参数 3. pytorch搭建vgg 4.flower_photos分类任务实践 5.资料 一、VGG解决的问题 论文链接&#xff1a;https://arxiv.org/pdf/1409.1556 在VGG之前&#xff0c;大多数深度学习模型相对较浅&#xff0c;比如下面的AlexNet(5层卷积和3…

记录阿里云部署gitlab

登录阿里云&#xff1a; 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 选择自己的ECS实例。我的实例是 使用VNC登录&#xff1a;输入用户名和密码 安装所需的依赖包&#xff1a; sudo yum install -y yum-utils device-mapper-persistent-data lvm2 添…

Git(分布式版本控制系统)(fourteen day)

一、分布式版本控制系统 1、Git概述 Git是一种分布式版本控制系统&#xff0c;用于跟踪和管理代码的变更&#xff0c;它由Linux、torvalds创建的&#xff0c;最初被设计用于Linux内核的开发。Git允许开发人员跟踪和管理代码的版本&#xff0c;并且可以在不同的开发人员之间进行…