MyBatis 详解

news2024/11/23 0:58:11

目录

1.MyBatis 框架的搭建

1.1 创建数据库和表

1.2 添加 MyBatis 依赖

1.3 设置 MyBatis 配置

1.3.1 设置数据库的连接信息

1.3.2 设置 XML 保存路径和命名格式

1.4 根据 MyBatis 写法完成数据库得操作

1.4.1 定义接口

1.4.2  使用 XML 实现接口

2.MyBatis查询操作

2.1 单表查询

2.1.1 参数占位符 #{ } 和 ${ }(常见面试问题)

从上边的事例可以看出 ${ } 可以实现的功能 #{ } 都能实现,并且 ${ } 还有 SQL 注入的问题,那么为什么 ${ } 的写法还存在?

2.1.2 ${ } 的优点

2.2 类中的属性和数据库表中的字段名不一致时,那么查询结果为 null,解决方案

2.2.1 将类中的属性和表中的字段名保持一致(最简单的解决方案)

2.2.2 使用 sql 语句中的 as 进行列名(字段名)重命名,让列名(字段名)等于属性名

2.2.3 返回字典映射:resultMap

2.3 like 查询操作

2.3.1 解决方案一:去掉单引号

2.3.2 解决方案二:mysql 内置函数 concat

3. 增、删、改操作

3.1 删除操作 + @Transactional 注解

3.2 修改用户操作

3.3 添加用户操作

3.3.1 MyBatis 添加操作,返回受影响的行数

3.3.2 返回自增 ID

4. 多表查询

4.1 一对一映射:使用注解

4.2 一对多映射:文章案例

5.动态 SQL

5.1 if 标签

5.2 trim 标签

5.3 where 标签

5.4 set 标签

5.5 foreach 标签


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

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

MyBatis 学习只分为两部分:

  • 配置 MyBatis 开发环境
  • 使用 MyBatis 模式和语法操作数据库

1.MyBatis 框架的搭建

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

1.2 添加 MyBatis 依赖

新创建的 Mybatis 启动时报错是正常的,因为未设置要连接的具体 MySQL 的详细信息

1.3 设置 MyBatis 配置

1.3.1 设置数据库的连接信息

application.properties:

#设置数据库的相关连接信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mycnblog2023?characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

1.3.2 设置 XML 保存路径和命名格式

MyBatis 的 XML 中保存是查询数据库的具体操作 SQL:

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

1.4 根据 MyBatis 写法完成数据库得操作

常规的写法,包含了两个文件:

  1. 接口:方法的声明(给其他层(service)调用)
  2. XML:实现接口

添加实体类:

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.4.1 定义接口

使用 @Mapper 注解,即数据持久层标志,相当于五大类注解中的 @Repository

package com.example.demo.dao;
import com.example.demo.model.Userinfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper //数据持久层的标志
public interface UserMapper {
    List<Userinfo> getAll();
}

1.4.2  使用 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.demo.dao.UserMapper">

</mapper>

注意namespace 是表明当前 xml 实现的是哪个接口的(不是使用 xml 文件名和接口进行匹配的)

UserMapper.xml 查询所有⽤户的具体实现 SQL:

<?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.dao.UserMapper">
    <select id="getAll" resultType="com.example.demo.model.Userinfo">
        select * from userinfo
    </select>
</mapper>

其中:

  • <mapper>标签:需要指定 namespace 属性,表示命名空间,值为 mapper 接⼝的全限定名,包括全包名.类名
  • id:方法名是和 Interface(接⼝)中定义的方法名称⼀样的),表示对接⼝的具体实现方法
  • id 中需要实现一个返回类型 resultType:是返回的数据类型,也就是开头我们定义的实体类
  • 写具体的 SQL 时,注意没有分号

使用单元测试验证是否生效:在需要生成单元测试类(UserMapper)中右键 generate,生成一个 Test

package com.example.demo.dao;
import com.example.demo.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 //告诉当前的测试程序,目前项目时运行在 Spring Boot容器中
class UserMapperTest {
    //Spring 容器就是 IoC 容器,实现 DI,就可以把测试类(UserMapper)注入进去,使用 @Autowired 注解
    @Autowired
    private UserMapper userMapper;

    @Test
    void getAll() {
        List<Userinfo> list = userMapper.getAll();
        System.out.println(list);
    }
}

其中:

  • 需要添加一个 @SpringBootTest 注解:告诉当前的测试程序,目前项目时运行在 Spring Boot 容器中
  • Spring 容器就是 IoC 容器,实现 DI,就可以把测试类(UserMapper)注入进去,使用 @Autowired 注解

mysql 中:

运行测试类,实现功能:


在这里,我介绍一种 MyBatis 插件:为了方便开发 MyBatis 实现 XML 和对应的接口之技安的快速跳转,可以安装一个 MyBatisX 插件

实现快速跳转: 

2.MyBatis查询操作

2.1 单表查询

根据 id 查询一条信息

 UserMapper 类:

@Mapper //数据持久层的标志
public interface UserMapper {
    //传递单个参数
    Userinfo getUserById(@Param("id") Integer id);
}
  • 传递参数需要使用包装类,不能使用基础类型(包装类可以接收 null,前端没有传递值不会报错;而基础类型会报错——500)
  • 传递非定义业务对象需要使用注解 @Param :给参数命名,比如在 mapper 里面某方法A(int id),当添加注解后A(@Param(“userId”) int id),也就是说外部想要取出传入的 id 值,只需要取它的参数名 userId 就可以了。

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.dao.UserMapper">
    <select id="getUserById" resultType="com.example.demo.model.Userinfo">
        select * from userinfo where id = ${id}
    </select>
</mapper>

测试类:

@SpringBootTest //告诉当前的测试程序,目前项目时运行在 Spring Boot容器中
class UserMapperTest {
    @Test
    void getUserById() {
        Userinfo userinfo =  userMapper.getUserById(1);
        System.out.println(userinfo.toString());
    }
}

传参有两种方式:1️⃣使用 ${ 参数名 }(使用 @value 注解读取配置文件的写法)

执行语句:

2️⃣使用 #{ 参数名 }

<?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.dao.UserMapper">
    <select id="getUserById" resultType="com.example.demo.model.Userinfo">
        select * from userinfo where id = #{id}
    </select>
</mapper>

  • 使用 jdbc 设置的 ?,? 是一个占位符,意味着 SQL 执行的时候将是预编译处理

2.1.1 参数占位符 #{ } 和 ${ }(常见面试问题)

  1. #{ }:预编译处理
  2. ${ }:字符直接替换
  • 预编译处理:MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 号,使用 PreparedStatement 的 set 方法来赋值
  • 直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值

预编译和直接替换区别:

  • 预编译:执行是安全的,可以防止 SQL 注入
  • 直接替换:执行是不安全的

SQL 注入:使用特殊的 SQL 语句完成非法操作

例如,进行用户名和密码的判断:

假设这是一个登陆的功能 ,必须要求用户名和密码都输入正确,才能返回一条 SQL 语句;

则 SQL  注入指的是密码输入错误也能查到数据

UserMapper 类:

@Mapper //数据持久层的标志
public interface UserMapper {
    //登陆功能
    Userinfo login(@Param("username") String username, @Param("password") String password);
}

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.dao.UserMapper">
    <select id="login" resultType="com.example.demo.model.Userinfo">
        select * from userinfo where username = '${username}' and password = '${password}'
    </select>
</mapper>

测试类:

@SpringBootTest //告诉当前的测试程序,目前项目时运行在 Spring Boot容器中
class UserMapperTest {
    @Test
    void login() {
        String username = "admin";
        String password = "' or 1='1";
        Userinfo userinfo = userMapper.login(username, password);
        System.out.println(userinfo.toString());
    }
}

在这里还有一个区别:

  • #{ } :使用在非数值类型或数值类型下是完全可以的
  • ${ }:非数值类型需要加 '    ' ,否则会报错

运行测试:

我们发现竟然可以查询出数据:输入不是正确密码,竟然能查询处结果(这就是最简单的 SQL 注入);则 使用 ${ } ,那么可以通过 ' or 1='1 这个命令登陆各种系统—— SQL 注入

使用 #{ }:

<?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.dao.UserMapper">
    <select id="login" resultType="com.example.demo.model.Userinfo">
        select * from userinfo where username = #{username} and password = #{password}
    </select>
</mapper>

MyBatis 在处理 #{ } 时,会将 SQL 中的 #{ } 替换为 ? 号,使用 PreparedStatement 的 set 方法来赋值


从上边的事例可以看出 ${ } 可以实现的功能 #{ } 都能实现,并且 ${ } 还有 SQL 注入的问题,那么为什么 ${ } 的写法还存在?

2.1.2 ${ } 的优点

例如,SQL 语句:

select * from userinfo order by id asc;
select * from userinfo order by id desc;
  • 在这种情况下,使用 ${ } 直接替换就行
  • 使用 #{ },asc、desc 就是一个占位符,并且它是一个 String,# 就会加单引号,即 'desc'、'asc',这是不可取的

  • ${ } 适用场景:当业务需要传递 SQL 命令,只能使用 ${ },不能使用 #{ }
  • ${ } 注意事项:如果要使用 ${ },那么传递的参数一定要能被穷举,否则不能使用(上述只能是 desc 或者 asc)

2.2 类中的属性和数据库表中的字段名不一致时,那么查询结果为 null,解决方案

已知数据库:

类中属性(Userinfo 类):

//添加实体类
@Data
public class Userinfo {
    private int id;
    private String name;
    private String password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private int state;
}
@Test
void getAllByOrder() {
    List<Userinfo> list = userMapper.getAllByOrder("desc");
    System.out.println(list);
}

UserMapper 类:

List<Userinfo> getAllByOrder(@Param("myOrder") String myOrder);

XML 配置:

<select id="getAllByOrder" resultType="com.example.demo.model.Userinfo">
    select * from userinfo order by id ${myOrder}
</select>

2.2.1 将类中的属性和表中的字段名保持一致(最简单的解决方案)

存在的问题: 定义的 name 已经在多个类中使用,那么把定义的 name 改为 username,随之其他类也需要改动,工作量有可能会增大

2.2.2 使用 sql 语句中的 as 进行列名(字段名)重命名,让列名(字段名)等于属性名

xml 配置修改 sql 语句:

<select id="getAllByOrder" resultType="com.example.demo.model.Userinfo">
    select id, username as name, password from userinfo order by id ${myOrder}
</select>

2.2.3 返回字典映射:resultMap

  • 字段名和程序中的属性名不同的情况,可使用 resultMap 配置映射
  • 一对一和一对多关系可以使用 resultMap 映射并查询数据

定义一个 resultMap,将属性名和字段名进行手动映射

UserMap.xml:

<resultMap id="BaseMap" type="com.example.demo.model.Userinfo">
    <id column="id" property="id"></id>
    <result column="username" property="name"></result>
    <result column="password" property="password"></result>
    <result column="createtime" property="createtime"></result>
    <result column="updatetime" property="updatetime"></result>
    <result column="state" property="state"></result>
</resultMap>

id 设置到查询结果中:

<select id="getAllByOrder" resultMap="BaseMap">
    select * from userinfo order by id ${myOrder}
</select>

2.3 like 查询操作

UserMapper 类:

//like 查询
List<Userinfo> getLikeList(@Param("username") String username);

UserMapper.xml:

<!--like 查询-->
<select id="getLikeList" resultType="com.example.demo.model.Userinfo">
    select * from userinfo where username like '%#{username}%'
</select>

生成单元测试:

@Test
void getLikeList() {
    String username = "三";
    List<Userinfo> list = userMapper.getLikeList(username);
    System.out.println(list);
}

  • 这个时候发现使用 like 的时候报错了,因为 #{username} 是预执行,会变成 ?,则在设置 问号 的时候会加单引号 ,相当于:select * from userinfo where username like '%'username'%';
  • 换成 ${ } 可以吗?可以,但是不能去用;因为使用 ${ }的场景:数值或者能被穷举或 过滤(风险也很大,关键东西多,过滤不彻底)

2.3.1 解决方案一:去掉单引号

<!--like 查询-->
<select id="getLikeList" resultType="com.example.demo.model.Userinfo">
    select * from userinfo where username like #{username}
</select>
String username = "%三%";

这个时候就不存在单引号中在嵌套一个单引号

2.3.2 解决方案二:mysql 内置函数 concat

例如:使用 concat 进行拼接

UserMapper.xml:

<!--like 查询-->
<select id="getLikeList" resultType="com.example.demo.model.Userinfo">
    select * from userinfo where username like concat('%', #{username}, '%')
</select>

单元测试:

String username = "三";

3. 增、删、改操作

  • <insert> 标签:插入语句
  • <update> 标签:修改语句
  • <delete> 标签:删除语句

3.1 删除操作 + @Transactional 注解

在 interface 中定义一个方法接口:

@Mapper //数据持久层的标志
public interface UserMapper {

    //删除操作
    int delById(@Param("id") Integer id);
}

Mapper.xml 实现标签:

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

删除操作不需要返回类型,可以不写 resultType

已知数据库:

生成测试类:

@SpringBootTest //告诉当前的测试程序,目前项目时运行在 Spring Boot容器中
class UserMapperTest {
    @Transactional //事务,单元测试添加 @Transactional,执行完成之后会进行回滚操作:既能实现测试功能,同时不会污染或影响数据库
    //已经构建了数据库,不能说测试的时候删除就没了
    @Test
    void delById() {
        int id = 2;
        int result = userMapper.delById(id);
        System.out.println("受影响的行数:" + result);
    }
}

添加一个注解 @Transactional,这是一个事务注解,执行完成之后会进行回滚操作,这样既能实现测试功能,同时不会污染或影响数据库(已经构建看数据库,不能说测试的时候删除就没了)

这个时候我们执行数据库操作发现这条语句依然存在:

没有 注解 @Transactional

  • 如果测试功能,并且不想影响数据库,那么就添加一个 @Transactional 注解(适合所有场景,不单单是删除操作)

3.2 修改用户操作

UserMapper 类:

//修改操作
int update(Userinfo userinfo);

传递对象不需要用 @Param

UserMapper.xml(修改操作也不需要返回类型,不写 resultType):

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

传递对象中的#{ }:括号中直接使用对象的属性名,不需要写 userinfo.username;Mybatis 框架已经默认做了这个过程

生成单元测试:

@SpringBootTest //告诉当前的测试程序,目前项目时运行在 Spring Boot容器中
class UserMapperTest {
    @Test
    void update() {
        Userinfo userinfo = new Userinfo();
        userinfo.setId(1);
        userinfo.setUsername("超级管理员");
        int result = userMapper.update(userinfo);
        System.out.println("受影响的行数:" + result);
    }
}

数据库:

当然如果不想影响数据库,和删除操作一样添加 @Transactional 注解

3.3 添加用户操作

3.3.1 MyBatis 添加操作,返回受影响的行数

UserMapper 类:

//修改操作
int update(Userinfo userinfo);

UserMapper.xml:

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

同理——传递对象中的#{ }:括号中直接使用对象的属性名,不需要写 userinfo.username;Mybatis 框架已经默认做了这个过程 

生成单元测试:

//添加操作
@Test
void add() {
    Userinfo userinfo = new Userinfo();
    userinfo.setUsername("张三");
    userinfo.setPassword("123");
    userinfo.setPhoto("/image/default.png");
    int result = userMapper.add(userinfo);
    System.out.println("受影响的行数:" + result);
}

数据库:

这个时候发现 id 好像不是很符合,接下来我们来设置 自增id

3.3.2 返回自增 ID

UserMapper 类:

//返回自增 ID
int insert(Userinfo userinfo);

UserMapper.xml:

<!--返回自增 id-->
<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
    insert into userinfo(username, password, photo) values(#{username}, #{password}, #{photo})
</insert>

  • useGeneratedKeys:这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键默认值:false
  • keyColum:数据库自增主键字段名(列名)
  • keyProoerty:指定能够唯⼀识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值

生成单元测试:

@Test
void insert() {
    Userinfo userinfo = new Userinfo();
    userinfo.setUsername("李四");
    userinfo.setPassword("123456");
    userinfo.setPhoto(" ");
    int result = userMapper.insert(userinfo);
    System.out.println("受影响的行数:" + result + " | ID:" + userinfo.getId());
}

4. 多表查询

4.1 一对一映射:使用注解

添加实体类:

package com.example.demo.model;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Articleinfo {
    private int id;
    private String title;
    private String content;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private int uid;
    private int rcount;
    private int state;
    //联表字段
    private String username;
}

注解实现(查询文章的所有信息和用户的username),在这里使用 @Select 注解写 sql 相当于 xml 中的 sql:

package com.example.demo.dao;
import com.example.demo.model.Articleinfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ArticleMapper {

    //查询注解
    //查询文章所有信息和用户的username
    @Select("select a.*,u.username from articleinfo a left join userinfo u on a.uid=u.id")
    List<Articleinfo> getAll();
}

生成测试类:

package com.example.demo.dao;
import com.example.demo.model.Articleinfo;
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 ArticleMapperTest {

    @Autowired
    private ArticleMapper articleMapper;
    @Test
    void getAll() {
        List<Articleinfo> list = articleMapper.getAll();
        System.out.println(list);
    }
}

4.2 一对多映射:文章案例

文章案例:查询一个用户的多篇文章

把查询分成两个查询:在业务类中首先查询到用户信息,再用用户的 id 去文章表里查询文章的 list,把 list 设置到文章用户的文章列表中

面试问项目有没有使用到多线程?上边案例就是如此,启动线程池,同时查询两张表(一张用户表,一张文章表),查询完成之后拼接实现查询

查询用户多篇文章,实体类 Userinfo 中添加字段:

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

    private List<Articleinfo> alist;
}

UserMapper 类:

@Select("select * from userinfo where id=#{id}")
Userinfo getUserById2(@Param("id") Integer id);

ArticleMapper 类:

@Select("select * from articleinfo where uid=#{uid}")
List<Articleinfo> getListByUid(@Param("uid") Integer uid);

单元测试(单线程实现):

@SpringBootTest //告诉当前的测试程序,目前项目时运行在 Spring Boot容器中
class UserMapperTest {

    //Spring 容器就是 IoC 容器,实现 DI,就可以把测试类(UserMapper)注入进去,使用 @Autowired 注解
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ArticleMapper articleMapper;
    @Test
    void getUserList() {
        int uid = 1;

        //1.根据 uid 查询 userinfo
        Userinfo userinfo = userMapper.getUserById2(uid);
        System.out.println(userinfo);

        //2.根据 uid 查询文章列表
        List<Articleinfo> list = articleMapper.getListByUid(uid);

        //组装数据
        userinfo.setAlist(list);
        System.out.println(userinfo);
    }
}

多线程:

@SpringBootTest //告诉当前的测试程序,目前项目时运行在 Spring Boot容器中
class UserMapperTest {

    @Test
    void getUserList() {
        int uid = 1;

        //定义线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100));

        final Object[] resultArray = new Object[2];
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                //1.根据 uid 查询 userinfo
                resultArray[0] = userMapper.getUserById2(uid);
            }
        });

        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                //2.根据 uid 查询文章列表
                resultArray[1] = articleMapper.getListByUid(uid);
            }
        });

        //组装数据(等线程池执行完成之后)
        while (threadPool.getTaskCount() != threadPool.getCompletedTaskCount()) {

        }

        Userinfo userinfo = (Userinfo) resultArray[0];
        userinfo.setAlist((List<Articleinfo>) resultArray[1]);
        System.out.println(userinfo);

    }
}

5.动态 SQL

5.1 if 标签

语法:
          <if test=" ">
               ....
          </if>
  • 使用场景 :在注册用户的时候,有可能分为两种注册信息,必填字段和非必填字段,如果在添加用户的时候不确定的字段传入,这时候就需要使用动态标签 <if> 来判断
<!--动态添加操作-->
<insert id="add2">
    insert into userinfo(username, password
    <if test="photo != null">
        ,photo
    </if>
    ) values(#{username},#{password}
    <if test="photo != null">
        ,#{photo}
    </if>
    )
</insert>

5.2 trim 标签

用来去掉不必要的信息;如果所有字段都是非必填项,就考虑使用 <trim> 标签结合 <if> 标签,对多个字段都采取动态生成的方式

<trim> 标签的属性:

  • prefix表示整个语句块,以 prefix 的值作为前缀
  • suffix:表示整个语句块,以suffix的值作为后缀
  • prefixOverrides:表示整个语句块要去除掉的前缀
  • suffixOverrides:表示整个语句块要去除掉的后缀
<!--trim 标签-->
<insert id="add3">
    insert into userinfo
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username!=null">
                username,
            </if>
            <if test="password!=null">
                password,
            </if>
            <if test="photo!=null">
                photo,
            </if>
        </trim>
    values
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="username!=null">
            #{username},
        </if>
        <if test="password!=null">
            #{password},
        </if>
        <if test="photo!=null">
            #{photo},
        </if>
    </trim>
</insert>
  • prefix 这个操作相当于在 trim 前面加 (
  • suffix 这个操作相当于在 trim 后面加 )
  • 多个 <if>组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于 suffixOverrides 配置去掉最后⼀个 

测试类:

@Test
void add3() {
    Userinfo userinfo = new Userinfo();
    userinfo.setUsername("王五");
    userinfo.setPassword("123");
    int result = userMapper.add2(userinfo);
    System.out.println("执行结果:" + result);
}

5.3 where 标签

  • 传⼊的用户对象,根据属性做 where 条件查询,用户对象中属性不为 null 的,都为查询条件
  • <where> 标签会判断里面是否有内容,如果有会动态生成 where 的 sql 语句;如果没有,则 where 关键字不会生成
  • 去除 最前面的 and 关键字

UserMapper 接⼝中新增条件查询方法:

//where 标签
List<Userinfo> getListByWhere(Userinfo userinfo);

UserMapper.xml 中新增条件查询 sql:

    <select id="getListByWhere" resultType="com.example.demo.model.Userinfo">
        select * from userinfo
        <where>
            <if test="id>0">
                id=#{id}
            </if>
            <if test="username!=null">
                and username=#{username}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
        </where>
    </select>

测试类1(什么都不传,相当于没有 where 语句):

    @Test
    void getListByWhere() {
        Userinfo userinfo = new Userinfo();
        List<Userinfo> list = userMapper.getListByWhere(userinfo);
        System.out.println(list);
    }

测试类2(传入id,正常添加 where语句):

    @Test
    void getListByWhere() {
        Userinfo userinfo = new Userinfo();
        userinfo.setId(1);
        List<Userinfo> list = userMapper.getListByWhere(userinfo);
        System.out.println(list);
    }

测试类3(where 语句可以去掉前面的 and):

    @Test
    void getListByWhere() {
        Userinfo userinfo = new Userinfo();
        //userinfo.setId(1);
        userinfo.setUsername("王五");
        //userinfo.setPassword("123");
        List<Userinfo> list = userMapper.getListByWhere(userinfo);
        System.out.println(list);
    }

上述 where标签也可以使用 trim 标签:

<trim prefix="where" prefixOverrides="and">
            
</trim>

trim 里边执行到了加 where,执行不到不加 where

5.4 set 标签

  • 根据传⼊的⽤户对象属性来更新⽤户数据,可以使⽤<set>标签来指定动态内容
  • <set> 标签会判断里面是否有内容,如果有会动态生成 set 的 sql 语句;如果没有,则 set 关键字不会生成
  • 去掉最后面的逗号(,)

UserMapper 接⼝中新增条件查询方法:

//set 标签
int update2(Userinfo userinfo);

UserMapper.xml 中新增条件查询 sql:

    <!--set 标签-->
    <update id="update2">
        update userinfo
        <set>
            <if test="username!=null">
                username=#{username},
            </if>
            <if test="password!=null">
                password=#{password},
            </if>
        </set>
        where id=#{id}
    </update>

测试类:

    @Test
    void update2() {
        Userinfo userinfo = new Userinfo();
        userinfo.setId(3);
        userinfo.setUsername("王五");
        int result = userMapper.update2(userinfo);
        System.out.println("执行结果:" + result);
    }

5.5 foreach 标签

<foreach>标签有如下属性:

  • collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
  • item:遍历时的每⼀个对象
  • open:语句块开头的字符串
  • close:语句块结束的字符串
  • separator:每次遍历之间间隔的字符串

根据多个⽂章 id 来删除⽂章数据。

ArticleMapper 中新增接口方法:

int deleteByIds(List<Integer> ids);

ArticleMapper.xml 中新增删除 sql:

<delete id="deleteByIds">
     delete from article
     where id in
     <foreach collection="list" item="item" open="(" close=")" separator=",">
         #{item}
     </foreach>
</delete>

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

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

相关文章

手记系列之七 ----- 分享Linux使用经验

前言 本篇文章主要介绍的关于本人在使用Linux记录笔记的一些使用方法和经验&#xff0c;温馨提示&#xff0c;本文有点长&#xff0c;约1.7w字&#xff0c;几十张图片&#xff0c;建议收藏查看。 一、Linux基础使用 1&#xff0c;服务器查看及时日志 tail -500f catalina.out …

npm的使用

package.json 快速生成package.json npm init -y “version”: “~1.1.0” 格式为&#xff1a;「主版本号. 次版本号. 修订号」。 修改主版本号是做了大的功能性的改动 修改次版本号是新增了新功能 修改修订号就是修复了一些bug dependencies "dependencies": {&…

计算机组成与结构-计算机体系结构

计算机体系结构 指令系统 Flynn分类法 SISD&#xff08;单指令流单数据流&#xff09; 结构 控制部分&#xff1a;一个处理器&#xff1a;一个主存模块&#xff1a;一个 代表 单处理器系统 SIMD&#xff08;单指令流多数据流&#xff09; 结构 控制部分&#xff1a;一个处理…

2022年09月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试&#xff08;1~6级&#xff09;全部真题・点这里 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 十六进制数100&#xff0c;对应的十进制数为 &#xff1f;&#xff08; &#xff09; A: 128 B: 256 C: 28 D: 56 答…

基于Qt命令行处理XML文件读写

Qt源码在后面,文本介绍Qt国际化语言和XML # XML基础(一) ## 1、概述 ### 1.1 定义(xml是个啥玩意儿?) XML(extensible Markup Language)俗称差妹儿,专业称之为:可拓展标记语言。 (1)何为标记,指的是一种标记语言,或者标签语言,即用一系列的标签来对数据进行…

Leetcode—421.数组中两个数的最大异或值【中等】明天写一下字典树做法!!!

2023每日刷题&#xff08;十九&#xff09; Leetcode—421.数组中两个数的最大异或值 算法思想 参考自灵茶山艾府 实现代码 class Solution { public:int findMaximumXOR(vector<int>& nums) {int maxValue *max_element(nums.begin(), nums.end());int highId…

STM32笔记-AD模数转换

目录 一、ADC介绍 二、ADC主要特征 三、ADC框图 1. ​​​​ 外部触发转换 ​ 2. 转换模式 3. 输入通道 4. 逻辑框图 四、校准 五、数据对齐 六、AD转换步骤 七、AD_Init(单通道AD转换)初始化函数配置 一、ADC介绍 1. 12位ADC是一种逐次逼近型模拟数字转换器。它有多达…

扣人心弦玄幻小说,故事爽度堪比热门IP,堪称史诗级作品

《万古不死&#xff0c;葬天&#xff0c;葬地&#xff0c;葬众生》 这本书是我看过最符合我心意的长生文。它始终在刻画一个长生者的经历&#xff0c;并且每一次的离别和悲伤都恰到好处。主角不是直接睡到无敌然后出去浪&#xff0c;而是不断学习&#xff0c;智商在线。坑杀雷族…

垃圾回收系统小程序定制开发搭建攻略

在这个数字化快速发展的时代&#xff0c;垃圾回收系统的推广对于环境保护和可持续发展具有重要意义。为了更好地服务于垃圾回收行业&#xff0c;本文将分享如何使用第三方制作平台乔拓云网&#xff0c;定制开发搭建垃圾回收系统小程序。 首先&#xff0c;使用乔拓云网账号登录平…

用户界面执行器设计场景-PR性能测试工具/压力测试工具

要使用 PerformanceRunner性能测试工具/压力测试工具测试您的系统&#xff0c;必须创建负载测试场景。场景定义每次测试期间发生的事件。场景定义并控制要模拟的用户数、这些用户执行的操作以及用于运行模拟场景的计算机。执行器负责设计场景、运行场景、控制场景、各种波形图生…

【广义表,树,满二叉树,完全二叉树,二叉树的存储结构】

文章目录 广义表树树的基本术语线性结构和树结构的比较二叉树的定义案例二叉树的性质和存储结构两种特殊形式下的二叉树满二叉树完全二叉树完全二叉树的性质 二叉树的存储结构二叉树的顺序存储结构二叉树的链式存储结构三叉链表 广义表 广义表&#xff08;又称Lists&#xff0…

建筑能源管理(8)——合同能源管理

1、简介 合同能源管理是20世纪70年代中期在发达国家逐步发展起来的一种节能服务机制在国外简称EPC(Energy Performance Contracting)&#xff0c;在国内广泛地被称为EMC (Energy Management Contracting)&#xff0c;它由专门的节能服务公司(Energy Service Company,ESCO)在为…

【KVM】软件虚拟化和硬件虚拟化类型

前言 大家好&#xff0c;我是秋意零。 今天介绍的内容是虚拟化技术以及软件虚拟化和硬件虚拟化。 &#x1f47f; 简介 &#x1f3e0; 个人主页&#xff1a; 秋意零&#x1f525; 账号&#xff1a;全平台同名&#xff0c; 秋意零 账号创作者、 云社区 创建者&#x1f9d1; 个…

Windows搭建Web站点:免费内网穿透发布至公网

目录 什么是cpolar&#xff1f; 概述 1. 注册并安装cpolar内网穿透 2. 搭建一个静态Web站点 2.1 下载演示站点 2.2 本地运行演示站点 2.3 本地浏览测试站点是否正常 3. 本地站点发布公网可访问 3.1 登录cpolar web ui管理界面 3.2 启动website隧道 3.3 获取公网URL地…

【数据结构】单链表详解(超详细)

单链表是我们学习数据结构时必不可少的部分&#xff0c;但也由于指针的参与变得更加复杂&#xff0c;这篇文章学习完之后可以更好地理解与掌握链表结构 注意&#xff1a; 数据结构中&#xff0c;不在乎菜单的创建&#xff0c;注重的是功能的实现&#xff1b;菜单的创建会影响我…

什么是协议栈? 用户态协议栈设计(udp协议栈)

什么是协议栈呢&#xff1f; &#xff08;协议栈&#xff08;Protocol Stack&#xff09;是计算机网络和通信系统中的一个重要概念&#xff0c;它指的是一组协议层的层次结构&#xff0c;这些协议层一起协同工作&#xff0c;以便在不同计算机或设备之间实现数据通信和交换。每…

开源 Wiki 软件 wiki.js

wiki.js简介 最强大、 可扩展的开源Wiki 软件。使用 Wiki.js 美观直观的界面让编写文档成为一种乐趣&#xff01;根据 AGPL-v3 许可证发布。 官方网站&#xff1a;https://js.wiki/ 项目地址&#xff1a;https://github.com/requarks/wiki 主要特性&#xff1a; 随处安装&a…

【从零开始学习Redis | 第四篇】基于延时双删对Cache Aside的优化

前言&#xff1a; 在如今的单体项目中&#xff0c;为了减轻大量相同请求对数据库的压力&#xff0c;我们采取了缓存中间件Redis。核心思想为&#xff1a;把数据写入到redis中&#xff0c;在查询的时候&#xff0c;就可以直接从Redis中拿取数据&#xff0c;这样我们原本对数据库…

Microsoft Dynamics 365 CE 扩展定制 - 5. 外部集成

本章内容包括: 使用.NET从其他系统连接到Dynamics 365使用OData(Java)从其他系统连接到Dynamics 365使用外部库从外部源检索数据使用web应用程序连接到Dynamics 365运行Azure计划任务设置Azure Service Bus终结点与Azure Service Bus构建近乎实时的集成使用来自Azure服务总线…

单目标应用:粒子群优化算法(PSO)求解微电网优化MATLAB

一、微网系统运行优化模型 微电网优化模型介绍&#xff1a; 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、粒子群优化算法&#xff08;PSO&#xff09;求解微电网优化 &#xff08;1&#xff09;部分代码 close all; clear ; clc; global P_load; %电负荷 gl…