MyBatis 的使用方法

news2024/11/23 15:21:51

观前提示:本篇博客演示使用的 IDEA 版本为2021.3.3版本,使用的是Java8(又名jdk1.8)

前端使用 VSCode(Visual Studio Code1.78.2)

电脑使用的操作系统版本为 Windows 10


目录

Mybatis是什么?

Mybatis 有什么用?

Mybatis 框架交流

Mybatis 项目环境搭建

1. 添加 Mybatis 框架支持

2.设置 Mybatis 配置信息

2.1 建立数据库

2.2 设置数据库连接的相关信息以及 MyBatis 的 xml 保存路径和 xml 命名模式

Mybatis 模式开发

后端开发思路

添加实体类

添加 Mapper 接口

添加 Mapper.xml

添加 Service

添加 Controller

启动项目

单元测试

进入单元测试

Mybatis 动态获取参数

参数占位符 #{} 和 ${}

# 和 $ 的区别

SQL 注入 

$  优点

改、删、增操作

1. 修改密码

2. 删除用户操作

3. 增加用户操作

特别的添加: 返回自增 id

查询操作

1. 模糊查询

2. 多表联查

并发查询

动态 SQL 

标签

标签

Mybatis 中多个都是非必传参数的解决方案

解决方案1: 1=1 解决方案

解决方案2: trim 的方式

解决方案3: 标签方式


Mybatis是什么?

MyBatis是一款优秀的Java持久层框架。它通过 XML 或注解的方式将要执行的 SQL 语句映射成为 Java 对象中的方法,然后利用 JDBC 执行 SQL 并将查

询结果映射为 Java 对象返回。MyBatis 的设计思想是将 SQL 语句与 Java 代码分离,通过 XML 或注解来定义 SQL 语句,这样不仅方便了开发人员对 SQL

语句进行管理和维护,而且使得 SQL 语句与 Java 代码解耦,提高了代码的可读性和可维护性。

Mybatis 有什么用?

对于后端开发来说,程序是由以下两个重要的部分组成的:

1. 后端程序

2. 数据库

使用 JDBC ,整个操作⾮常的繁琐,我们不但要拼接每⼀个参数,⽽且还要按照模板代码的⽅式,⼀步步的操作数据库,并且在每次操作完,还要⼿动关

闭连接等,⽽所有的这些操作步骤都需要在每个⽅法中重复书写。

MyBatis 可以帮助我们更⽅便、更快速的操作数据库。

Mybatis 框架交流

MyBatis 是⼀个 ORM 框架,ORM(Object Relational Mapping),即对象关系映射。在⾯向对象编程语⾔中,将关系型数据库中的数据与对象建⽴起

映射关系,进⽽⾃动的完成数据与对象的互相转换:

1. 将输⼊数据(即传⼊对象)+SQL 映射成原⽣ SQL

2. 将结果集映射为返回对象,即输出对象

ORM 把数据库映射为对象:

数据库表(table)--> 类(class)

记录(record,⾏数据)--> 对象(object)

字段(field) --> 对象的属性(attribute)

⼀般的 ORM 框架,会将数据库模型的每张表都映射为⼀个 Java 类。也就是说使⽤ MyBatis 可以像操作对象⼀样来操作数据库中的表,可以实现对象和

数据库表之间的转换。

Mybatis 项目环境搭建

1. 添加 Mybatis 框架支持

还是先创建一个 Spring-Boot项目, 把Spring Boot DevTools, Lombok, Spring Web 勾选上

再找到 SQL, 把MyBatis Framework 和 MySQL Driver 勾选上

到目前为止,一个带有 Mybatis 的 Spring-Boot项目已经创建完成

虽然 MyBatis 项目创建完成了, 但是启动的时候是必然会失败的

原因很简单, 没有连接数据库

2.设置 Mybatis 配置信息

在 application.properties 里面进行连接数据库操作, 在连接之前需要先建立数据库

2.1 建立数据库

-- 创建数据库
drop database if exists bolgtest;
create database bolgtest DEFAULT CHARACTER SET utf8mb4;

-- 使用数据数据
use bolgtest;

-- 创建表[用户表]
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 `bolgtest`.`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);

数据库建立成功就会提示 Query OK ,1 row affected

只要最后一行都显示这个,前面就不用看了

2.2 设置数据库连接的相关信息以及 MyBatis 的 xml 保存路径和 xml 命名模式

#1.链接数据库
spring.datasource.url=jdbc:mysql://localhost:3306/bolgtest?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
# 注意!!! password 要写自己本机mysql密码,不是固定的111111
spring.datasource.password=111111
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 2.设置MyBatis
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml
#打印 mybatis 执行 SQL
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置打印
logging.level.com.example.demo=debug

注意: 

 

 

 

 

Mybatis 模式开发

后端开发思路

下⾯按照后端开发的⼯程思路,也就是下⾯的流程来实现 MyBatis 查询所有⽤户的功能

本篇博客建立分层就是按照上图进行建立, 不过是先建立实体类, 在按照 Mapper -> Servic -> Controller 的顺序建立 

添加实体类

在 com.example.demo 建立 Entity, 再在 Entity  里建 UserEntity 类

实体类里面有什么东西, 取决于数据库里面有什么东西

把数据库 userinfo 里面的信息全部录入到 UserEntity 里面, 剩下的 mybatis 自己会解决

package com.example.demo.Entity;

import lombok.Data;

import java.time.LocalDateTime;

@Data
public class UserEntity {
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private Integer state;
}

添加 Mapper 接口

注意: Mapper 接口是 interface, 让其它层可以注入使用的接口

在 com.example.demo 建立 Mapper, 再在 Mapper 里建 UserMapper 接口

package com.example.demo.Mapper;

import com.example.demo.Entity.UserEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

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

添加 Mapper.xml

注意: 具体实现 sql(它是 interface 的 "实现")

在 resource 里建立 Mybatis, 再在 Mybatis里建 UserMapper.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.Mapper.UserMapper">
    <select id="getAll" resultType="com.example.demo.Entity.UserEntity">
        select * from userinfo
    </select>
</mapper>

注意: 

 

添加 Service

package com.example.demo.Service;

import com.example.demo.Entity.UserEntity;
import com.example.demo.Mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public List<UserEntity> getAll() {
        return userMapper.getAll();
    }
}

添加 Controller

package com.example.demo.Controller;

import com.example.demo.Entity.UserEntity;
import com.example.demo.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/getall")
    public List<UserEntity> getAll() {
        return userService.getAll();
    }
}

注意: URL 在写的时候绝对不要出现大写字母, 可以出现 阿拉伯数字甚至汉字

 比如.哔哩哔哩的视频 URL 它就是出现阿拉伯数字, 也不会出现大写字母

我拿必应搜索百度, 可以看到他的 URL 出现了汉字在里面, 但是依然没有大写字母 

因为: RFC 1738 针对早期的 Web 环境制定了 URL 基本格式的标准。该标准要求 URL 中的字母必须采用 US-ASCII 字符集,并限制所有大写字符转换为相

应的小写字符。而 RFC 3986 则对之前的规范做了更新和扩展,对字符集的要求进行了更加严格的规定,同时保留了大小写敏感的部分。因此,为了遵守

URL 的标准规范,在 URL 中使用大写字母可能会导致 URL 不被正确解析或无法访问。

说白了就是:乌龟的屁股------龟腚

全部分层建立完

 

启动项目

返回服务器

 

成功使用带有 MyBatis 的 Spring-Boot 项目通过 URL 进行访问

给数据库在添加一条数据,看看还能不能通过 URL 访问到

-- 添加一个测试信息
INSERT INTO `bolgtest`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES 
(2, 'zhangsan', 'zhangsan', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);

查询数据库, 发现两条信息

 

 

刷新 URL , 这边也可以查询到

 

单元测试

单元测试的优点

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

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

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

4. 可以绕过用户登录验证进行单元测试

进行单元测试需要 在 pom.xml 引入依赖,好消息是不需要管他, 因为他自己已经引入了

 每一个 Spring-Boot 的项目都会自带单元测试

 junit 和 mockito 都是自带的

进入单元测试

先把测试需要的代码写上

usermapper

package com.example.demo.Mapper;

import com.example.demo.Entity.UserEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

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

    //根据 id 查询用户对象
    UserEntity getUserById(@Param("id") Integer id);
}

// @Param 来自于 ibatis, 是 MyBatis 的前身
// 用于指定 SQL 语句中的参数名称和类型。MyBatis 框架会根据 @Param 注解来映射参数的名称和值,方便实现动态 SQL。

usermapper.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.Mapper.UserMapper">
    <select id="getAll" resultType="com.example.demo.Entity.UserEntity">
        select * from userinfo
    </select>

    <select id="getUserById" resultType="com.example.demo.Entity.UserEntity">
        select * from userinfo where id=${id}
    </select>

</mapper>

 

再要测试的类上面右键 Generate 生成

 

 

勾选完,他会自动生成类,可以看到他在 Test 里面 

 只勾选一个

 因为 UserEntity 里面有 @Data, 所以可以直接打印

package com.example.demo.Mapper;

import com.example.demo.Entity.UserEntity;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest// 表示当前单元测试的类是运行在 Spring Boot 环境中
class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    void getUserById() {
        //查询zhangsan,所以里面是放2
        UserEntity user = userMapper.getUserById(2);
        System.out.println(user);
    }
}

运行测试类 

 

上次只测试了 getUserById, 现在测试 getAll, 生成测试的时候会报错,问你是否还在这个测试类里面继续生成新的测试,选 OK

 

 

 测试代码

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

运行测试类

 

观察上面两个单元测试会发现, 每次只运行一个,所以里面可以有多个

可以使用断言,但是不常用

Mybatis 动态获取参数

参数占位符 #{} 和 ${}

1. ${paramName} -> 直接替换

2. #{paramName} -> 占位符替换

 这三行就可以看出来 $ 和 #

 

 

# 和 $ 的区别

先使用 #

mapper

//根据名称查询用户对象
UserEntity getUserByName(@Param("username") String username);

mapper.xml

<select id="getUserByName" resultType="com.example.demo.Entity.UserEntity">
        select * from userinfo where username=#{username}
    </select>

生成单元测试

@Test
    void getUserByName() {
        UserEntity user = userMapper.getUserByName("zhangsan");
        System.out.println(user);
    }

 

再使用 $

只有 mapper.xml 需要修改

<select id="getUserByName" resultType="com.example.demo.Entity.UserEntity">
        select * from userinfo where username=${username}
    </select>

重新运行单元测试

抛出异常 

使用 $ 会报错,显示找不到 zhangsan Unknown column 'zhangsan' in 'where clause'

$ 是所见即所得,不会进行预处理,加上 '   ' 可以解决问题,但是有 SQL 注入问题

<select id="getUserByName" resultType="com.example.demo.Entity.UserEntity">
        select * from userinfo where username='${username}'
    </select>

此时进行单元测试,一切正常

SQL 注入 

使用最简单的 SQL 注入, 需要先删除一条数据,因为多条数据他有 bug

 

 

使用登录来进行 SQL 注入演示

Mapper

//登录
UserEntity login(UserEntity user);

Mapper.xml

<select id="login" resultType="com.example.demo.Entity.UserEntity">
         select * from userinfo where username='${username}' and password='${password}'
    </select>

单元测试

@Test
    void login() {
        String username = "admin";
        String password = "admin";
        UserEntity inputUser = new UserEntity();
        inputUser.setUsername(username);
        inputUser.setPassword(password);
        UserEntity user = userMapper.login(inputUser);
        System.out.println(user);
    }

现在开始 SQL 注入

@Test
    void login() {
        String username = "admin";
        String password = "' or 1='1";
        UserEntity inputUser = new UserEntity();
        inputUser.setUsername(username);
        inputUser.setPassword(password);
        UserEntity user = userMapper.login(inputUser);
        System.out.println(user);
    }

 

原因: SQL 注入问题的出现,主要是因为在拼接 SQL 语句时,未对输入数据进行充分的过滤和转义处理。

这条 SQL 语句的条件部分中含有一个逻辑表达式 '1'='1',这个表达式的值永远为真。因此,不管数据库中是否存在名为 admin 的用户,这条 SQL 语句

都会返回用户表中的所有记录,甚至可能返回所有表中的记录。这就是 SQL 注入攻击的原理。

 

解决方法:

使用 #

当有可能存在 SQL 注入攻击时,MyBatis 会自动将参数进行转义处理,从而避免了通过插入恶意代码来实现 SQL 注入攻击的可能性。

<select id="login" resultType="com.example.demo.Entity.UserEntity">
        select * from userinfo where username=#{username} and password=#{password}
    </select>

 

$  优点

Mapper

    List<UserEntity> getAllByIdOrder(@Param("ord") String ord);

Mapper.xml

<select id="getAllByIdOrder" resultType="com.example.demo.Entity.UserEntity">
        select * from userinfo order by id ${ord}
    </select>

单元测试

@Test
    void getAllByIdOrder() {
        List<UserEntity> list = userMapper.getAllByIdOrder("desc");
        System.out.println(list.size());
    }

使用 #, 单元测试就会报错

 

使⽤ ${sort} 可以实现排序查询,⽽使⽤ #{sort} 就不能实现排序查询了,因为当使⽤ #{sort} 查询时,如果传递的值为 String 则会加单引号,就会导致 sql

错误。

改、删、增操作

1. 修改密码

mapper

//修改密码
    int updatePassword(@Param("id") Integer id,
                       @Param("password") String password,
                       @Param("newPassword") String newPassword);

mapper.xml

<update id="updatePassword">
        update userinfo set password=#{newPassword}
        where id=#{id} and password=#{password}
    </update>

单元测试

@Transactional
    //事务,保证不会污染数据库(代码执行完,rollback 回滚)
    //可以加载类上,也可以加载方法上
    @Test
    void updatePassword() {
        int result = userMapper.updatePassword(1, "admin", "123456");
        System.out.println("修改: " + result);
    }

 事务成功回滚

2. 删除用户操作

mapper

//删除用户
    int delById (@Param("id")Integer id);

 

mapper.xml

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

单元测试

@Transactional
    @Test
    void delById() {
        int id = 1;
        int result = userMapper.delById(id);
        System.out.println("删除结果: " + result);
    }

 

3. 增加用户操作

mapper

//新增用户
    int addUser(UserEntity user);

mapper.xml

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

单元测试

@Test
    void addUser() {
        UserEntity user = new UserEntity();
        user.setUsername("zhangsan");
        user.setPassword("123456");
        int result = userMapper.addUser(user);
        System.out.println("添加: " + result);
    }

 

 

特别的添加: 返回自增 id

mapper

//添加返回影响行数和id
    int addUserGetId(UserEntity user);

 

mapper.xml

<insert id="addUserGetId" useGeneratedKeys="true" keyProperty="id">
        <!--useGeneratedKeys 是否自动生成 id, 默认是 false-->
        <!--keyProperty 自动生成 分配给 id-->
        insert into userinfo(username,password) values(#{username},#{password})
    </insert>

 

单元测试

@Test
    void addUserGetId() {
        UserEntity user = new UserEntity();
        user.setUsername("lisi");
        user.setPassword("123456");
        int result = userMapper.addUserGetId(user);
        System.out.println("添加结果: " + result);
        System.out.println("ID: " + user.getId());
    }

 

 

查询操作

1. 模糊查询

mapper

//根据用户模糊查询
    List<UserEntity> getListByName(@Param("username") String username);

mapper.xml

<select id="getListByName" resultType="com.example.demo.Entity.UserEntity">
        select id,username,password as pwd from userinfo where username like concat ('%',#{username},'%')
    </select>

这个写法,使用了 concat 进行拼接, 组成一个模糊匹配的查询条件,即查询所有用户名中包含指定字符串的用户信息。

单元测试

@Test
    void getListByName() {
        String username = "zhang";
        List<UserEntity> list = userMapper.getListByName(username);
        list.stream().forEach(System.out::println);
    }

 

2. 多表联查

新建一个实体类 ArticleInfo

package com.example.demo.Entity;

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

在entity 建立一个 vo 在新建一个实体类 ArticleInfoVo

package com.example.demo.Entity.vo;

import com.example.demo.Entity.ArticleInfo;
import lombok.Data;

//作者名
@Data
public class ArticleInfoVO extends ArticleInfo {
    private String username;

    @Override
    public String toString() {
        return "ArticleInfoVO{" +
                "username='" + username + '\'' +
                "} " + super.toString();
    }
}

mapper

新建一个 ArticleMapper 

package com.example.demo.Mapper;

import com.example.demo.Entity.vo.ArticleInfoVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface ArticleMapper {
    // 查询文章详情
    ArticleInfoVO getDetail(@Param("id") Integer id);
}

mapper.xml

对于的新建一个 ArticleMapper.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.Mapper.ArticleMapper">

    <select id="getDetail" resultType="com.example.demo.Entity.vo.ArticleInfoVO">
        select a.*,u.username from articleinfo a
        left join userinfo u on u.id=a.uid
        where a.id=#{id}
    </select>

    
</mapper>

 

文章表和详情表进行多表联查

单元测试

package com.example.demo.Mapper;

import com.example.demo.Entity.vo.ArticleInfoVO;
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 ArticleMapperTest {
@Autowired
private ArticleMapper articleMapper;

    @Test
    void getDetail() {
        ArticleInfoVO articleInfoVO = articleMapper.getDetail(1);
        System.out.println(articleInfoVO);
        System.out.println("title:" + articleInfoVO.getTitle());
    }
}

并发查询

数据库新增文章数据

 

 mapper

//并发查询
    List<ArticleInfoVO> getListByUid(@Param("uid")Integer id);

 

mapper.xml

<select id="getListByUid" resultType="com.example.demo.Entity.vo.ArticleInfoVO">
        select a.*,u.username from articleinfo a
        left join userinfo u on u.id=a.uid
        where a.uid=#{uid}
    </select>

单元测试

@Test
    void getListByUid() {
        Integer uid = 1;
        List<ArticleInfoVO> list = articleMapper.getListByUid(uid);
        list.stream().parallel().forEach(System.out::println);
}

这是并发

list.stream().forEach(System.out::println);

这是不并发

动态 SQL 

动态 SQL 是指在构建 SQL 语句时,根据需要动态地生成 SQL 语句的一种技术。它使用程序的控制流程和条件逻辑来决定 SQL 语句的组成

部分,从而实现更加灵活和可变的数据库操作。

与静态 SQL 相比,动态 SQL 能够根据不同的条件生成不同的 SQL 语句,从而适应更加复杂和多变的业务需求。常见的场景包括根据用

户输入的参数进行数据查询、动态生成复杂的查询条件、动态生成排序规则等。

在 Java 开发中,常用的动态 SQL 技术包括使用 PreparedStatement 对象进行参数化查询、使用 MyBatis 框架的动态 SQL 功能等。这些技

术都可以帮助开发人员灵活地构建 SQL 语句,从而提高代码的灵活性和可维护性。

需要注意的是,动态 SQL 也可能会带来一些性能问题,如在运行时动态生成 SQL 语句可能会影响查询效率。因此,在实际应用中,需要

权衡灵活性和性能之间的关系,并谨慎使用动态 SQL 技术。

<if> 标签

回到 UserMapper

mapper

//动态 SQL
    int addUser2(UserEntity user);

 

mapper.xml

<insert id="addUser2">
        insert into userinfo(username,
        <if test="photo!=null and photo!='' ">
            photo,
        </if>
        password)values(#{username},
        <if test="photo!=null and photo!=''">
            #{photo},
        </if>
        #{password})
    </insert>

 

单元测试

@Transactional
    @Test
    void addUser2() {
        String username = "liliu";
        String password = "123456";
        UserEntity user = new UserEntity();
        user.setUsername(username);
        user.setPassword(password);
        user.setPhoto("cat.png");
        int result = userMapper.addUser2(user);
        System.out.println("添加: " + result);
    }

 

<trim> 标签

之前的插⼊用户功能,只是有⼀个 sex 字段可能是选填项,如果所有字段都是⾮必填项,就考虑使用 <trim> 标签结合 <if> 标签,对多个字

段都采取动态生成的⽅式。

<trim>标签中有如下属性:

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

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

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

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

mapper

//trim 演示
    int addUser3(UserEntity user);

mapper.xml

<insert id="addUser3">
        insert into userinfo
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username!=null and username!='' ">
                username,
            </if>
            <if test="photo!=null and photo!='' ">
                photo,
            </if>
            <if test="password!=null and password!='' ">
                password,
            </if>
        </trim>
        values
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username!=null and username!='' ">
                #{username},
            </if>
            <if test="photo!=null and photo!=''">
                #{photo},
            </if>
            <if test="password!=null and password!='' ">
                #{password},
            </if>
        </trim>
    </insert>

单元测试

@Transactional
    @Test
    void addUser3() {
        String username = "liliu";
        String password = "123456";
        UserEntity user = new UserEntity();
        user.setUsername(username);
        user.setPassword(password);
        int result = userMapper.addUser3(user);
        System.out.println("添加: " + result);
    }

 

Mybatis 中多个都是非必传参数的解决方案

解决方案1: 1=1 解决方案

mapper

List<ArticleInfoVO> getListByIdOrTitle(@Param("id")Integer id,
                                           @Param("title")String title);

mapper.xml

<select id="getListByIdOrTitle" resultType="com.example.demo.Entity.vo.ArticleInfoVO">
        select * from articleinfo
        where 1=1
        <trim prefixOverrides="and">
            <if test="id!=null and id>0">
                and id=#{id}
            </if>
            <if test="title!=null and title!=''">
                and title like concat('%',#{title},'%')
            </if>
        </trim>
    </select>

 

解决方案2: trim 的方式

mapper.xml

<select id="getListByIdOrTitle" resultType="com.example.demo.Entity.vo.ArticleInfoVO">
        select * from articleinfo
        <trim prefix="where" suffixOverrides="and">
            <if test="id!=null and id>0">
                id=#{id} and
            </if>
            <if test="title!=null and title!=''">
                title like concat('%',#{title},'%')
            </if>
        </trim>
    </select>

 

当 trim 中生成代码, 那么才会添加 <trim> 里面的前缀和后缀的, 如果 trim 中没有生成代码, 则前缀和后缀都会省略

单元测试

@Test
    void getListByIdOrTitle() {
        List<ArticleInfoVO> list = articleMapper.getListByIdOrTitle(null,null);
        System.out.println(list.size());
    }

 

解决方案3: <where> 标签方式

mapper.xml

 <select id="getListByIdOrTitle" resultType="com.example.demo.Entity.vo.ArticleInfoVO">
        select * from articleinfo
        <where>
            <if test="id!=null and id>0">
                id=#{id}
            </if>
            <if test="title!=null and title!=''">
                and title like concat('%',#{title},'%')
            </if>
        </where>
    </select>

单元测试

@Test
    void getListByIdOrTitle() {
        List<ArticleInfoVO> list = articleMapper.getListByIdOrTitle(1,null);
        System.out.println(list.size());
    }

 


本文完,感谢观看,不足之处请在评论区指出 !

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

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

相关文章

设计模式之生成器(建造者)模式笔记

设计模式之建造者模式笔记 说明Builder(生成器)目录UML生成器(建造者)模式示例类图自行车类建造者抽象类摩拜单车对象类小黄车单车对象类指挥者类测试类优缺点 模式扩展手机类测试类 说明 记录下学习设计模式-生成器(也叫建造者)模式的写法。 Builder(生成器) 意图:将一个复…

监控中的计算机科学

文章目录 一、前言二、监控分类2.1 模拟摄像头2.1 数字摄像头 三、监控系统四、后端产品4.1 硬盘录像机4.2 视频矩阵4.3 控制设备4.4 显示设备 五、传输端5.1 光纤视频线5.1.2 单模光纤5.1.3 多模光纤5.1.4 光端机 5.1 双绞线 六、畅享 一、前言 布林肯访华&#xff0c;黑我们…

Redis6之事务与锁

事务 Redis事务是一个单独的隔离操作&#xff1a;事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中&#xff0c;不会被其他客户端发送来的命令请求所打断。 Redis事务的主要作用就是串联多个命令防止别的命令插队。 命令 1、开启事务&#xff1a;multi 2、执行事…

STM32 RGB屏幕

使用ST的HAL库进行开发&#xff0c;RGB屏幕是480*272的4.3寸LCD&#xff0c;由于驱动RGB屏幕需要较多的内存&#xff0c; 所以使用了外部SDRAM&#xff0c;内存是32M字节&#xff0c;关于SDRAM的驱动本文不进行讨论。 RGB屏幕常用的像素格式有&#xff1a;ARGB8888、RGB888、…

Vue----Vue项目的目录结构

【原文链接】Vue----Vue项目的目录结构 Vue 项目的目录结构 VUE项目的目录结构如下所示 .vscode VSCode工具的配置文件&#xff0c;和VUE项目没有什么关系 node_modules VUE项目运行依赖文件&#xff0c;通过npm install 安装的文件即存放在此文件夹 public 资源文件夹&am…

【现代密码学】(网安)期末复习笔记

现代密码学 【考后感悟】还是得注重简答题&#xff0c;需每个密码算法都要有所了解&#xff08;有些难的可以不用了解完整算法过程&#xff0c;估计考不上&#xff1f;&#xff09;&#xff0c;并对几个重要密码算法&#xff08;重点下面会讲&#xff09;着重复习&#xff08;会…

部署kubernets v1.27.3集群

本文将演示如何使用kubeadm快速部署一个Kubernetes v1.27.1集群&#xff0c;并会简单说明如何在集群上部署nginx容器 主机环境预设 本示例中的Kubernetes集群部署将基于以下环境进行。 OS: Ubuntu 20.04 Kubernetes&#xff1a;v1.27.3 Container Runtime: Docker CE 23.0.…

【并发编程】深入探索AQS

文章目录 一、AQS 介绍二、通过ReentrantLock分析AQS的实现2.1、获取锁流程2.2、获取锁源码分析2.2.1、acquire2.2.2、tryAcquire2.2.3、addWaiter2.2.4、acquireQueued2.2.5、shouldParkAfterFailedAcquire 2.3、解锁源码分析2.3.1、unlock2.3.2、release2.3.3、tryRelease2.3…

实验篇(7.2) 17. 站对站安全隧道 - FortiGate作为SSL客户端(SSL) ❀ 远程访问

【简介】虽然常用的站到站的连接用的是IPsec VPN&#xff0c;但是在某些特殊情况下&#xff0c;UDP500或4500端口被阻断&#xff0c;IPsec VPN无法连接&#xff0c;那么还有其它办法实现站到站的连接吗&#xff1f;SSL VPN也可以的。 实验要求与环境 OldMei集团深圳总部部署了域…

NodeJS File Upload⑩

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言文件上传 后端接口 Form表单上传 Axios前后端分离上传 实现效果演示 记录 读取图片文件总结 ✨文章有误请指正&#xff0c;如果觉得对你有用&a…

RK3568平台开发系列讲解(外设篇)四线风扇驱动实验

🚀返回专栏总目录 文章目录 一、硬件连接二、原理图分析三、驱动适配3.1、内核配置3.2、修改设备树3.3、实验沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将讲解四线风扇的使用。 一、硬件连接 风扇模块如下所示,黑线是负,红线是正,黄线是测速,蓝线是…

算法——字符串匹配算法——BM(Boyer-Moore) 算法

字符串匹配算法——BM &#xff08;Boyer-Moore&#xff09; 算法 概述场景一 坏字符场景且模式串中没有匹配字符场景二 坏字符场景且模式串中有匹配字符场景三 好后缀场景且模式串中没有匹配字符场景四 好后缀场景且模式串中有匹配字符场景五 好后缀场景且模式串中有匹配子串后…

EfficientDet-pytorch目标检测训练

目录 1. EfficientDet-pytorch版本代码下载 2.数据集准备 2.1数据集格式 2.2 定义自己数据集的yml文件 3. 训练配置 4.模型评估 5.测试模型性能 1. EfficientDet-pytorch版本代码下载 GitHub - zylo117/Yet-Another-EfficientDet-Pytorch: The pytorch re-implement…

chatgpt赋能python:Python提取指定数据的方法与技巧

Python提取指定数据的方法与技巧 在SEO优化中&#xff0c;数据的提取和分析是非常重要的环节之一。而Python具有方便易用的数据处理能力&#xff0c;成为了SEO优化工程师们的重要工具之一。本文将介绍Python中提取指定数据的方法与技巧&#xff0c;以及实现的具体案例。 数据…

python:使用Scikit-image库进行单波段遥感图像颜色直方图特征提取(histogram)

作者:CSDN @ _养乐多_ 本文记录了使用Scikit-image库对单波段遥感图像做颜色直方图特征提取的代码。 文章目录 一、颜色直方图特征详解二、代码一、颜色直方图特征详解 颜色直方图是一种用于描述图像中颜色分布的特征表示方法。它将图像中每个像素的颜色值作为输入,统计并显…

Java 基础进阶篇(十七):反射概述及获取对象的方式

文章目录 一、反射概述二、反射获取类对象三、反射获取构造器对象四、反射获取成员变量对象五、反射获取方法对象六、 反射的作用6.1 绕过编译阶段为集合添加数据6.2 通用框架的底层原理 一、反射概述 反射是指对于任何一个Class类&#xff0c;在 “运行的时候”&#xff0c;不…

PHP实战开发23-PHP结合Nginx获取用户真实IP地址

文章目录 一、前言二、关于用户IP的背景知识2.1 HTTP请求2.2 HTTP代理服务器2.3 X-Forwarded-For头部 三、代码实现3.1 Nginx配置3.2 PHP代码处理 总结 一、前言 本文已收录于PHP全栈系列专栏&#xff1a;PHP快速入门与实战 在Web应用程序中&#xff0c;IP地址是常见的数据项…

Flink 学习四 Flink 基础架构

Flink 学习四 Flink 基础架构&算子链&槽位 文章大部分数据来源 : https://nightlies.apache.org/flink/flink-docs-release-1.14/docs/concepts/flink-architecture/ Flink 是一个分布式系统,需要有效的分配和管理计算资源才可以执行流式程序; 集成了常见的资源管理…

chatgpt赋能python:Python简介

Python简介 Python是一种高级编程语言&#xff0c;具有易读性和简洁性的特点。它被广泛使用于Web开发、数据科学、人工智能、机器学习和自动化测试等领域。Python也是一种非常适合新手学习编程的语言。 在本篇文章中&#xff0c;我们将讨论如何使用Python提取指定内容以进行S…

【BMS】电池包硬件方案选型指南

🔋电池包硬件方案选型指南🔋 BMS硬件系统需求主要包括:测温模块、测流模块、测压模块、系统电源、保护电路、故障检测电路,本文阐述各个功能模块在不同场景下的电池包硬件系统方案选择。 一、测温 NTC(热敏电阻) 电池包测温一般包括表皮温度、内部温度、PCB温度(极片布…