当前位置: 首页 > news >正文

MyBatis查询数据库【秘籍宝典】

  • 0.MyBatis执行流程
  • 1.第一个MyBatis查询
    • 1.创建数据库和表
    • 1.2.添加MyBatis框架依赖【新项目】
    • 1.3.添加MyBatis框架依赖【旧项目】
    • 1.4.配置连接数据库
    • 1.5.配置MyBatis的XML路径
    • 2.MyBatis模式开发
    • 2.1 添加MyBatis的xml配置
  • 3.增查改删(CRUD)
    • 5.1.增加操作
    • 5.2.修改操作
    • 5.3 删除操作
  • 6.查询操作
    • 6.1单表查询
  • 6.2 参数占位符 #{} 与 ${} 区别
    • 6.2.1.SQL注入问题
    • 6.2.2 like查询
    • 6.2.3 属性名和字段名不一致
  • 6.3 resultType
    • 6.3.1 resultMap
    • 6.3.2 多表查询
  • 7.动态SQL
    • 7.1 < if >标签
    • 7.2 < trim >标签
    • 7.3 < where >标签
    • 7.4 < set >标签
    • 7.5 < foreach >标签

  • MyBatis是什么?

MyBatis 是⼀款优秀的持久层框架,它⽀持⾃定义 SQL、存储过程以及⾼级映射。MyBatis 去除了⼏乎所有的 JDBC 代码以及设置参数和获取结果集的⼯作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接⼝和 Java POJO(Plain Old Java Objects,普通⽼式 Java 对象)作为数据库中的记录。通过使用MyBatis,开发人员可以方便地执行CRUD操作(创建、读取、更新和删除),以及复杂的数据库查询和存储过程的调用。

【MyBatis官网】

简单来说,MyBatis是一个简单、灵活和强大的持久层框架和数据库交互的工具,广泛应用于Java开发中,使得数据访问变得更加便捷和高效。

0.MyBatis执行流程

在这里插入图片描述

大概流程:前端发生Ajax请求给控制器,控制器调用服务层,服务层进行编排,服务层调用mybatis,mybatis调用数据库,再逐层返回。

MyBatis:在interface中声明方法,在xml中实现方法。mybatis是基于这两个实现的。

1.第一个MyBatis查询

MyBatis 也是⼀个 ORM 框架,ORM(Object Relational Mapping),即对象关系映射。在⾯向

对象编程语⾔中,将关系型数据库中的数据与对象建⽴起映射关系,进⽽⾃动的完成数据与对象的互相转换:

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

  2. 将结果集映射为返回对象,即输出对象ORM 把数据库映射为对象:

  • 数据库表(table)–> 类(class)
  • 记录(record,⾏数据)–> 对象(object)
  • 字段(field) --> 对象的属性(attribute)

⼀般的 ORM 框架,会将数据库模型的每张表都映射为⼀个 Java 类。

也就是说使⽤ MyBatis 可以像操作对象⼀样来操作数据库中的表,可以实现对象和数据库表之间

的转换。

1.创建数据库和表

打开MySQL客户端进行登录后,直接拷贝下面代码复制粘贴就行。

代码如下:

-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;-- 使用数据数据
use mycnblog;-- 创建表[用户表]
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 `mycnblog`.`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框架依赖【新项目】

在创建新Spring Boot项目时,添加依赖:

在SQL中添加MyBatis Framework MySQL Driver

在这里插入图片描述

1.3.添加MyBatis框架依赖【旧项目】

在旧项目中添加新依赖:

<!-- 添加 MyBatis 框架 -->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version>
</dependency>
<!-- 添加 MySQL 驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.38</version><scope>runtime</scope>
</dependency>

1.4.配置连接数据库

application.yml添加如下内容:

spring:datasource:url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver# 设置MyBatis
mybatis:mapper-locations:- classpath:/mybatis/*Mapper.xml#打印MyBatis 执行SQLconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:level:com.example.demo: debug

注意:如果使用mysql-connector-java是5.x之前的,使用的是“ com.mysql.jdbc.Driver ” ;如果是⼤于 5.x,使⽤的是“ com.mysql.cj.jdbc.Driver ” 。

  • 有些主机&useSSL=false 会报错,设置成true就行。
  • username,password要写自己mysql的用户名的和密码。
  • 如果不配置,启动项目是报错:

在这里插入图片描述

1.5.配置MyBatis的XML路径

application.yml添加如下内容:

# 设置MyBatis
#保存路径 /mybatis
#保存文件后缀 名 Mapper.xml
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml
  • classpath:当前项目的根路径
  • classpath:/mybatis:当前项目的根路径下的文件夹叫mybatis
  • *Mapper.xml:所有与mybatis相关的xml文件都叫做某某Mapper.xml。比如与用户有关的叫做UserMapper.xml

2.MyBatis模式开发

在这里插入图片描述

MyBatis模式开发由两部分组成:

  • Interface:其他层可以注入使用的接口。
  • .xml:实现Interface的方法,而SQL语句就在xml文件中。

运行到MyBatis这块,Mybatis会生成一个代理对象,代理对象会将interface的方法声明和xml中的方法实现组合成代理对象的方法进行填充。实际上服务层调用MyBatis时调用的就是这个代理对象,代理对象就是一个普通类,普通类自然而然就有方法和方法实现。

1.新建一个mybatis文件夹:

在这里插入图片描述

2.添加实体类:

package com.example.demo.entity;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;import java.time.LocalDateTime;@Data //自动添加set和get方法
public class UserInfoEntity {private Integer id;private String username;private String password;private String photo;//时间格式化@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")private LocalDateTime createTime;private LocalDateTime updateTime;
}

注意:属性名和数据库中的字段名保持一致,MyBatis会自动把类和数据库中的数据关联。

3.创建Mapper:

在demo下创建mapper包,再创建相应的Interface【添加@Mapper注解

在这里插入图片描述

添加接口方法,用于查询:

package com.example.demo.mapper;import com.example.demo.entity.UserInfoEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper //注意添加注解
public interface UserMapper {List<UserInfoEntity> getAll();
}
  • 没有在xml中实现接口中的方法,会默认报错。

2.1 添加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.mapper.UserMapper"></mapper>
  • namespace路径要与当前xml实现的接口路径一致。含全包名,类名
  • 在每个xxxMapper.xml中都需要添加这个xml配置!

4.在mybati文件夹下创建一个xxxMapper.xml,此处创建UserMapper.xml,用于查询用户操作。

添加sql语句:

   <select id="getAll" resultType="com.example.demo.entity.UserInfoEntity">select * from userinfo</select>
  • id :当前xml实现的接口中需要实现的方法的名字。
  • resultType:返回结果,当前xml实现的接口中对应的方法返回的对象的路径。(如果方法返回的是一个集合,只用写集合里的对象)

5.添加Service:

咱们说,服务器是调用的,所以使用Service调用:

package com.example.demo.service;import com.example.demo.entity.UserInfoEntity;
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 {@Autowiredprivate UserMapper userMapper;public List<UserInfoEntity> getAll() {return userMapper.getAll();}
}
  • 注入 uerMapper:MyBatis允许使用Interface,因为最终实现这行的是由代理对象来填充的。
  • Service层调用,然后使用xml文件来实现Interface里的方法具体实现。

6.添加Controller:

package com.example.demo.controller;import com.example.demo.entity.UserInfoEntity;
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 {@AutowiredUserService userService;@RequestMapping("/getAll")public List<UserInfoEntity> getAll(){return userService.getAll();}
}

7.运行项目,输入url地址
在这里插入图片描述

3.增查改删(CRUD)

⽤户的增加、删除和修改的操作,对应使⽤ MyBatis 的标签如下:

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

5.1.增加操作

mapper 接口方法 实现代码:

    int add(@Param("username") String username,@Param("password") String password);

xml的sql语句具体实现:

默认情况下返回的是受影响的⾏号。

<insert id="add">insert into userinfo(username,password)value(#{username},#{password})
</insert>
  • #{}是替换符。

service代码:

public int add(@Param("username") String username, @Param("password") String password){return userMapper.add(username,password);
}

controller代码:

@RequestMapping("/add")
public int add(@Param("username") String username, @Param("password") String password){return userService.add(username,password);
}

重新运行项目,输入url,输入参数:

在这里插入图片描述

查询数据库,看是否添加成功:
在这里插入图片描述

5.2.修改操作

因为除了controller里的代码和sql语句会有些不同,其余层代码基本一致【照着增加操作模仿】,所以以下只写controller代码和SQL语句:

Controller代码:

@RequestMapping("/update")
public int update(@Param("id")Integer id,@Param("username") String username){return userService.update(id,username);
}

XML中的SQL语句:

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

运行结果:

在这里插入图片描述

在这里插入图片描述

5.3 删除操作

其他代码雷同,SQL语句:

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

6.查询操作

6.1单表查询

实现根据用户id查询用户信息的功能:

Controller代码如下:

@RequestMapping("/getuser")
public UserInfoEntity getUserById(Integer id) {return userService.getUserById(id);
}

XML的SQL语句实现如下:

<select id="getUserById" resultType="com.example.demo.entity.UserInfoEntity">select * from userinfo where id=#{id}
</select>

6.2 参数占位符 #{} 与 ${} 区别

在MyBatis中,#{}${} 是参数占位符,用于在SQL语句中插入参数值。它们的主要区别在于如何处理参数。

  1. #{} 预编译处理:

    • MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使⽤ PreparedStatement

      的 set ⽅法来赋值。

    • 使用 #{} 时,MyBatis会将参数值作为预编译的参数进行处理,可以有效防止SQL注入攻击。

    • #{} 会自动进行参数类型转换和安全处理,使得传递参数更加方便和安全。

    • #{} 可以直接在SQL语句中使用,例如 WHERE column_name = #{paramName}

  2. ${} 直接替换处理:

    • MyBatis 在处理 ${} 时,就是把 ${} 简单的替换成变量的值,不做任何的特殊处理,比如 ${}不会自动添加 ''号,而#{}会自动加上''号。
    • 使用 ${} 时,MyBatis会将参数值直接拼接到SQL语句中,类似于字符串替换。
    • ${} 不会对参数进行安全处理或类型转换,可能存在SQL注入风险。
    • ${} 可以用于动态生成SQL语句的部分内容,例如表名、列名等。

综上所述,#{}${} 的区别主要在于参数的处理方式和安全性:

  • #{} 是推荐的使用方式,能够提供更好的安全性和可读性,并且支持参数类型转换。
  • ${} 可以用于一些特殊场景,例如动态生成SQL语句的部分内容,但需要注意潜在的SQL注入问题。

在编写MyBatis的SQL语句时,请根据具体需求选择合适的参数占位符,以确保代码的安全和稳定性。

请注意,有些特殊情况下(例如在动态SQL中),可能需要结合使用 #{}${} 来实现更灵活的功能。

6.2.1.SQL注入问题

SQL注入是一种常见的安全漏洞,攻击者可以在应用程序中的输入点插入恶意的SQL代码来执行未经授权的操作或获取敏感数据。以下是一个简单的SQL注入示例:

假设有一个简单的登录功能,用户可以通过输入用户名和密码进行身份验证:

@RequestMapping("/islogin")
public UserInfoEntity isLogin(String username,String password){return userService.isLogin(username,password);
}

XML代码:

<select id="isLogin" resultType="com.example.demo.entity.UserInfoEntity">select * from userinfo where username='${username}' and password='${password}'
</select>

在上述代码中,使用直接替换的方式将用户输入内容直接替换到SQL查询语句中。如果攻击者在用户名或密码输入框中输入恶意的值,就可以构造出恶意的SQL语句,例如:

localhost:8080/user/islogin?username=lisi&password='or 1=1 and id = '5

假设MySQL的userinfo表如下:
在这里插入图片描述

使用postman演示:

在这里插入图片描述

最终构成的代码是:

SELECT * FROM userinfo WHERE username = 'lisi' AND password = ''or 1=1 and id = '5'
  • 上述sql语句表示,查询userinfo表,username是lisi, 并且 password为空;或者 1=1 并且id=5。username=’lisi‘为真,password是空为假,但是1=1and id=5 为真,于是不需要密码就查询到了结果。

这个恶意的SQL语句绕过了正常的身份验证逻辑,使得查询条件始终为真,因此可以绕过登录功能并返回所有用户的数据。要防止SQL注入攻击,可以使用参数化查询(使用#{}参数占位符)或预编译语句来构建SQL语句。

6.2.2 like查询

简单使用#{} 进行like查询会无结果的:

    <select id="findUserByName" resultType="com.example.demo.entity.UserInfoEntity">select * from userinfo where username like '%#{username}%'</select>

在上述示例中,#{username}是占位符,%是通配符。如果传递username值为“lisi”相当于:select * from userinfo where username like '%'lisi'%';是查询不到结果的。

进行like查询,需要使用#{},加上配合mysql内置函数 concat()来实现:

<select id="findUserByName" resultType="com.example.demo.entity.UserInfoEntity">select * from userinfo where username like concat('%',#{usernam
e});
</select>

在上述示例中,#{username}是占位符,可以通过参数传递具体的值。使用CONCAT()函数将占位符和%通配符连接在一起,实现以指定值开头的模糊匹配。

6.2.3 属性名和字段名不一致

如果是增、删、改返回受影响的⾏数,那么在 mapper.xml 中是可以不设置返回的类型的,因为默认是返回受影响的行数,如下图所示:

在这里插入图片描述

但是,查询操作不设置返回类型,则会报错:

    <select id="getById" >select * from userinfo where id=#{id}</select>

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

显示运行了一个查询但没有找到结果映射,也就是说对于< select >查询标签至少需要两个属性:

  • id属性:用于标识实现接口中的某个方法;
  • 结果映射属性:有两种实现标签< resultMap>< resultType>

6.3 resultType

大多数情况下都可以使用resultType,原因是使用方便,直接定义到某个实体类的路径就行;

    <select id="getAll" resultType="com.example.demo.entity.UserInfoEntity">select * from userinfo</select>

表示将查询结果映射到UserInfoEntity对象。

6.3.1 resultMap

resultMap使用场景:

  • 字段名和程序中属性名不一致,可使用resultMap配置映射。
  • 一对一和一对多关系,可以使用resultMap映射并查询数据。
<resultMap id="userResultMap" type="com.example.User"><id property="id" column="user_id"></id><result property="username" column="user_name"><]</result><result property="email" column="user_email"></result>><!-- 其他映射关系 --><!-- ..... --></resultMap><select id="getAll" resultMap="userResultMap">select * from userinfo
</select>

在上述示例中,<resultMap>定义了User类与查询结果的映射关系。具体解释如下:

  • property 属性:property属性指定Java对象属性的名称。
  • column 属性:column属性指定数据库表中对应字段的列名。
  • id 属性:主键
  • result属性:普通字段和属性

通过配置合适的<resultMap>,可以灵活地定义不同查询的映射关系,以满足特定业务需求。

使用时,只需要将ResultMap配置的id名,添加到相应的位置,如上述代码中< select >标签

java类属性如下:
在这里插入图片描述

数据库字段如下:
在这里插入图片描述

虽然属性和字段名不一致,但通过上述resultMap配置后,一样可以成功映射:

在这里插入图片描述

6.3.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 updatatime;private int uid;@Overridepublic String toString() {return "ArticleInfo{" +"id=" + id +", title='" + title + '\'' +", content='" + content + '\'' +", createtime=" + createtime +", updatatime=" + updatatime +", uid=" + uid +'}';}
}

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;@Overridepublic String toString() {return "ArticleInfoVO{" +"username='" + username + '\'' +"} " + super.toString();}
}

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

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.mapper.ArticleMapper"><select id="getDetail" resultType="com.example.demo.entity.vo.ArticleInfoVO">select a.*,u.username from articleinfo aleft join userinfo u on u.id=a.uidwhere a.id=#{id}</select>
</mapper>

Test测试类:

这是一个单元测试的类:

在这里插入图片描述

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.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class ArticleMapperTest {@Autowiredprivate ArticleMapper articleMapper;@Testvoid getDetail() {ArticleInfoVO articleInfoVO = articleMapper.getDetail(2);System.out.println(articleInfoVO);}
}

测试类运行结果:

在这里插入图片描述

7.动态SQL

MyBatis提供了强大的动态SQL功能,可以根据不同的条件来动态生成SQL语句。大白话就是允许我们在xml进行条件判断。

【mybatis】

7.1 < if >标签

假设在注册一个账号时,需要输入账户和密码【必填字段】,而性别可以选择性填写【非必填字段】,那么如果有不确定的字段传入,这个时候就需要使用动态标签< if >来判断了,比如性别sex可为非必填字段,具体实现如下:

<insert id="add">insert into userinfovalue(#{username},#{password},<if test="sex != null">sex = #{sex},</if>#{age})</insert>

在上述示例中,<if>标签被嵌套在<inser>语句内部。<if>标签中的test属性指定了条件表达式,test中传入的是对象属性,满足条件时才会包含对应的SQL代码。

在示例中,如果传入的userinfo对象的sex属性不为空,则生成一个sex = #{sex},的条件;这样就可以根据不同情况拼接不同的查询条件。

<if>标签还支持更复杂的条件判断,可以使用比较运算符、逻辑运算符、调用对象方法等。

需要注意的是,在使用<if>时需要遵循以下几点:

  • 条件表达式必须是合法的OGNL表达式。
  • 在条件表达式中使用null时,要使用" != null"进行判断,不能直接使用"=="。
  • 条件表达式中的字符串比较要注意处理空字符串,比如<if test="name != null and name != ''">

7.2 < trim >标签

<trim>标签是用于处理SQL语句中多余的空格和逗号的XML元素。它可以根据需要添加或移除SQL语句中的前缀、后缀、前后缀或者连接词。

<trim>标签有以下属性可用:

  • prefix:在SQL语句开头添加的前缀。
  • prefixOverrides:需要移除的前缀内容。
  • suffix:在SQL语句末尾添加的后缀。
  • suffixOverrides:需要移除的后缀内容。

下面是一个使用<trim>标签的示例:

<select id="getUserList" parameterType="com.example.User" resultType="com.example.User">SELECT * FROM usersWHERE id =#{id} and <trim prefix="AND" prefixOverrides="OR"><if test="name != null and name != ''">OR name = #{name}</if><if test="age != null">OR age = #{age}</if></trim>
</select>

在上述示例中,<trim>标签被嵌套在<where>语句内部。<trim>标签根据指定的条件添加或移除相应的前缀和前缀内容。

在示例中,如果传入的User对象的name属性不为空,则生成一个前缀为"AND"的条件,并移除前缀中多余的"OR",也就是AND name = #{name};如果age属性不为空,则生成一个前缀为"AND"的条件,并移除前缀中多余的"OR",也就是AND age = #{age}。这样可以动态地拼接多个条件。当然也可以拼, 号;(号、)号。

比如:

<insert id="add">insert into user
<trim prefix="(" suffix=")" suffixOverrides=","><if test="username != null">username,</if><if test="password != null">password,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="username != null">#{username},</if><if test="password != null">#{password},</if></trim>
</insert>

上述代码:在第一个< trim>标签,在语句开始部分会加上(,如果传入的username不为空,则会生成一个username并把,去掉,如果传入的password不为空,则会生成一个password并把,去掉,最后加上)

在第二个< trim>标签,在语句开始部分会加上values (,如果传入的username不为空,则会生成一个username并把,去掉,如果传入的password不为空,则会生成一个password并把,去掉,最后加上)

7.3 < where >标签

<where>标签是用于动态生成SQL语句中的WHERE。它可以根据条件判断自动生成和连接各个查询条件,并且能够处理多余的逻辑运算符(如ANDOR)。

下面是一个使用<where>标签的示例:

<select id="getUserList" parameterType="com.example.User" resultType="com.example.User">SELECT * FROM users<where><if test="name != null and name != ''">AND name = #{name}</if><if test="age != null">AND age = #{age}</if></where>
</select>

在上述示例中,<where>标签包含了两个<if>标签,每个<if>标签表示一个查询条件。当条件满足时,该条件会被添加到WHERE子句中。

如果传入的User对象的name属性不为空,则生成一个条件 name = #{name},会自动把多余的AND去掉;如果age属性不为空,则生成一个条件AND age = #{age},此处的AND是有效的,就不会去掉了。

  • 使用<where>标签能够动态生成合理的WHERE子句,避免因为没有查询条件而导致的无效或错误的SQL语句。

  • 需要注意的是,<where>标签会自动处理多余的逻辑运算符,例如只有一个条件时,生成的SQL语句不会包含前置的AND

7.4 < set >标签

<set>标签是用于动态生成UPDATE语句中SET子句的XML元素。它可以根据条件判断自动生成要更新的字段和对应的值,并且能够处理多余的逗号(,)。

下面是一个使用<set>标签的示例:

<update id="updateUser" parameterType="com.example.User">UPDATE users<set><if test="name != null and name != ''">name = #{name},</if><if test="age != null">age = #{age},</if></set>WHERE id = #{id}
</update>

在上述示例中,<set>标签包含了两个<if>标签,每个<if>标签表示一个要更新的字段和对应的值。再次强调test传入的就是对象属性;当条件满足时,该字段和值会被添加到SET子句中。

如果传入的User对象的name属性不为空,则生成一条更新语句name = #{name},;如果age属性不为空,则生成一条更新语句age = #{age},,并会自动的去掉最后一个逗号(,)。

  • 使用<set>标签能够动态生成合理的SET子句,避免因为没有更新字段而导致的无效或错误的SQL语句。同时,<set>标签会自动处理多余的逗号,确保生成的UPDATE语句的语法正确。

  • 需要注意的是,在编写UPDATE语句时,<set>标签应该位于WHERE子句之前。

7.5 < foreach >标签

<foreach>标签是用于在SQL语句中循环遍历集合或数组的XML元素。它可以方便地将集合或数组中的元素插入到SQL语句中,生成批量操作的语句。

对集合进⾏遍历时可以使⽤该标签。< foreach >标签有如下属性:

  • collection:指定要遍历的集合或数组,如 List,Set,Map或数组对象

  • item:指定在每次迭代中将集合或数组的元素赋值给的变量名。

  • index:指定在每次循环迭代中,集合中的当前索引将被赋值给的变量。

  • open:语句块开头的字符串

  • close:语句块结束的字符串

  • separator:指定每个循环迭代之间的分隔符。

下面是一个使用<foreach>标签的示例:

<insert id="addList">INSERT INTO users (name, age) VALUES<foreach collection="list" item="user" separator=",">(#{user.name}, #{user.age})</foreach>
</insert>

在上述示例中,<foreach>标签是在INSERT语句中遍历List类型的参数list。每次迭代都将取出集合中的元素赋值给user变量,然后将该元素的属性nameage插入到VALUES子句中。

  • 通过使用<foreach>标签,可以轻松实现对集合或数组进行批量操作,如批量插入、批量更新等。

  • 需要注意的是,<foreach>标签也支持一些其他属性,例如index属性表示当前迭代的索引位置,openclose属性用于定义在循环的开头和结尾添加的字符串。

以下是一个使用openclose属性的示例:

<select id="getUsersByIdList" parameterType="java.util.List" resultType="com.example.User">SELECT * FROM usersWHERE id IN<foreach collection="list" item="id" open="(" close=")" separator=",">#{id}</foreach>
</select>

open属性定义了循环的开头字符串,它会在整个循环的最前面被插入。一般情况下,我们可以将它用来添加开始的括号、引号等。如果不需要在开头添加任何字符串,可以将open属性设置为空字符串或省略不写。

在上述示例中,我们想要查询一组用户的信息,这组用户的ID存储在一个List类型的参数 list中。通过使用<foreach>标签,我们将每个ID作为查询条件的一部分,将它们以逗号分隔放在括号内。

假设list中包含了三个ID,那么生成的SQL语句将类似于:

SELECT * FROM users WHERE id IN (1, 2, 3);

通过在<foreach>标签中使用openclose属性,我们可以方便地添加括号使查询条件得到正确的语法结构。

相关文章:

MyBatis查询数据库【秘籍宝典】

0.MyBatis执行流程1.第一个MyBatis查询1.创建数据库和表1.2.添加MyBatis框架依赖【新项目】1.3.添加MyBatis框架依赖【旧项目】1.4.配置连接数据库1.5.配置MyBatis的XML路径2.MyBatis模式开发2.1 添加MyBatis的xml配置 3.增查改删&#xff08;CRUD&#xff09;5.1.增加操作5.2.…...

目标检测舰船数据集整合

PS&#xff1a;大家如果有想要的数据集可以私信我&#xff0c;如果我下载了的话&#xff0c;可以发给你们~ 一、光学数据集 1、 DIOR 数据集(已下载yolo版本)&#xff08;论文中提到过&#xff09; DIOR由23463张最优遥感图像和190288个目标实例组成&#xff0c;这些目标实例用…...

第一章 Android 基础--开发环境搭建

文章目录 1.Android 发展历程2.Android 开发机器配置要求3.Android Studio与SDK下载安装4.创建工程与创建模拟器5.观察App运行日志6.环境安装可能会遇到的问题7.练习题 本专栏主要在B站学习视频&#xff1a; B站Android视频链接 本视频范围&#xff1a;P1—P8 1.Android 发展历…...

【LeetCode周赛】2022上半年题目精选集——二分

文章目录 2141. 同时运行 N 台电脑的最长时间解法1——二分答案补充&#xff1a;求一个int数组的和&#xff0c;但数组和会超int 解法2——贪心解法 2251. 花期内花的数目解法1——二分答案代码1——朴素二分写法代码2——精简二分⭐ 解法2——差分⭐⭐⭐ 2258. 逃离火灾解法1—…...

vuejs如何将线上PDF转为base64编码

只需要两个方法-下载与转换&#xff1a; 下载方法&#xff1a; demoDownloadPDF(url) {// if (!(/^https?:/i.test(url))) return;if (window.XMLHttpRequest) var xhr new XMLHttpRequest(); else var xhr new ActiveXObject("MSXML2.XMLHTTP");xhr.open(GET, u…...

Repo工作原理及常用命令总结——2023.07

文章目录 1. 概要2. 工作原理2.1 项目清单库(.repo/manifests)2.2 repo脚本库(.repo/repo)2.3 仓库目录和工作目录2.4 repo 目录结构分析 3. 使用介绍3.1 init3.2 sync3.3 upload3.4 download3.5 forall3.6 prune3.7 start3.8 status 4. 使用实践4.1 对项目清单文件进行定制4.2…...

Python教程(2)——开发python常用的IDE

为什么需要IDE 在理解IDE之前&#xff0c;我们先做以下的实验&#xff0c;新建一个文件&#xff0c;输入以下代码 total_sum 0 for x in range(1,101):total_sum x print(total_sum)非常非常简单的一个程序&#xff0c;主要就是计算1加到100的值&#xff0c;我们将它重命名…...

【lambda函数】lambda()函数

lambda&#xff08;&#xff09; lambda&#xff08;&#xff09;语法捕捉列表mutable lambda 底层原理函数对象与lambda表达式 lambda&#xff08;&#xff09;语法 lambda表达式书写格式&#xff1a; [capture-list] (parameters) mutable -> return-type{ statement }咱…...

ThreeJs CSS3DObject 点击失效问题

想实现一个在选中物体&#xff0c;弹出菜单&#xff0c;结果发现&#xff0c;点击会失效 <ul id"menu" class"list-group list-group-full"><li class"list-group-item" onclick"test()">24小时曲线</li><li cla…...

飞书深诺、恒生面试(部分)(未完全解析)

飞书深诺 说一下你对SaaS项目的理解&#xff1f;数据隔离是怎么处理的&#xff1f;Answer: 我们采用的是SAAS服务多租户数据隔离架构中的1.3共享数据库&#xff0c;通过租户ID来隔离&#xff0c;成本最低&#xff0c;隔离级别最低。Q&#xff1a;有没有开发隔离的中间件&#x…...

Spring Cloud Config: 了解、原理和使用

Spring Cloud Config: 了解、原理和使用 Spring Cloud Config 是 Spring Cloud 生态系统中的一个重要组件&#xff0c;它提供了一种分布式配置管理的解决方案&#xff0c;能够集中管理应用程序的配置&#xff0c;支持多种后端存储&#xff0c;如 Git、SVN、本地文件系统、Vaul…...

基于图的路径规划算法对比

基于图的路径规划算法对比 算法说明与实现效果构造路网1.打开Arcmap2.新建Shapefile文件3.编辑Shapefile属性4.开始编辑5.创建要素并绘制路网6.打断相交线7.保存编辑8.打开图层属性表9.添加字段10.完成字段添加11.计算字段id12.计算点线字段13.选中length字段14.计算length字段…...

SQL Server 索引

1、索引的概念 假设数据库中现在有2万条记录&#xff0c;现在要执行这样一个查询&#xff1a;SELECT * FROM table where num10000。如果没有索引&#xff0c;必须遍历整个表&#xff0c;直到num等于10000的这一行被找到为止&#xff1b;如果在num列上创建索引&#xff0c;SQL …...

java抽奖

目录 一、简要描述 二、代码 一、简要描述 此抽奖方式为&#xff1a;在1~30个数字之间 挑选7个不重复的数字输入&#xff0c;系统会根据中奖的号码与用户输入的号码进行比较&#xff0c;系统会输出是否中奖的提示&#xff01; 二、代码 import java.util.Scanner; import ja…...

【springboot+云计算】B/S医院信息管理系统源码(云HIS)

一、基于云计算技术的B/S架构的医院管理系统(简称云HIS) 采用前后端分离架构&#xff0c;前端由Angular框架、JavaScript语言开发&#xff1b;后端使用Java语言开发。系统遵循服务化、模块化原则开发&#xff0c;具有强大的可扩展性&#xff0c;二次开发方便快捷。为医疗机构提…...

go 读写 excel 读取 txt 繁体中文转码

读取txt&#xff0c;繁体中文转码 package mainimport ("bufio""fmt""golang.org/x/text/encoding/traditionalchinese""os" )func readTxtTest() {txtPath : C:\Users\admin\Desktop\contact.txtfile, err : os.Open(txtPath)if err…...

docker网卡的IP地址修改

1. 安装docker 请参考 Linux系统在线安装docker任意版本完整教程 2. dockers启动一个容器查看容器ip docker run -d --name nginx -p 80:80 nginx #启动一个容器 docker ps -a #查看容器正常运行 docker inspect --format {{ .NetworkSettings.IPAddress }} nginx ##查看…...

python与深度学习——基础环境搭建

一、安装jupyter notebook Jupyter Notebook是一个开源的交互式笔记本环境&#xff0c;可以用于编写和执行代码、创建可视化效果、展示数据分析结果等。我们在这里用它实现代码运行和观察运行结果。安装jupyter notebook实质上是安装Anaconda,后续还要在Anaconda Prompt中使用c…...

Django实现简单的音乐播放器 2

在《Django实现简单的音乐播放器 1》前期准备的基础上开始开发。 效果&#xff1a; 目录 项目视图 创建视图方法 路由加载视图 加载模板 创建首页html文件 加载静态资源文件 加载静态文件 使用方法 启动服务器 加载数据表 创建表模型 生成表迁移 执行创建表 插入…...

OpenCV 入门教程:图像读取和显示

OpenCV 入门教程&#xff1a;图像读取和显示 导语一、图像读取1.1、导入 OpenCV 库1.2、读取图像文件1.3、图像读取的返回值 二、图像显示2.1、创建窗口2.2、图像显示2.3、等待按键2.4、关闭窗口 三、示例应用总结 导语 在计算机视觉和图像处理领域&#xff0c;读取和显示图像…...

什么是GPT?

文章目录 1、什么是GPT&#xff1f;2、gpt版本时间线3、我们能用GPT做什么&#xff1f;4、如何快速体验GPT&#xff1f;5、作为一名开发者&#xff0c;如何在代码中使用GPT&#xff1f;6、如何在现有项目中使用和部署GPT&#xff1f;7、GPT的优缺点&#xff1f;8、对于人工智能…...

如何通过浏览器配置哪些网页不走代理服务器,Lantern开启后部分网页打不开了

浏览器点设置 > 搜索“代理” > “打开计算机的代理设置” > 编辑“使用代理服务器” 搜索“代理” > “打开计算机的代理设置” > 编辑“使用代理服务器”&#xff0c;将不用代理的url链接域名写进来&#xff0c;点击保存。然后刷新打不开的网页&#xff0c;…...

Redis常见面试题

什么是Redis持久化&#xff1f;Redis有哪几种持久化方式&#xff1f;优缺点是什么 把redis内存中的数据持久化到磁盘的过程就是redis持久化。RDB:快照存储&#xff0c;每隔一段时间对redis内存中的数据进程快照存储。优点:恢复数据快 缺点:数据完整性差 AOF:日志追加 把每个写…...

应用零信任原则:案例研究和现场经验教训

随着云架构、软件即服务和分布式劳动力日益成为当今现代组织的主导现实&#xff0c;零信任安全模型已成为首选安全范例。 因此&#xff0c;描述零信任安全原则以及构成零信任架构 (ZTA) 的组件的出版物和资源数量几乎令人瘫痪。该行业缺乏的是一个多样化的示例库&#xff0c;可…...

RabbitMQ系列(14)--Topics交换机的简介与实现

1、Topics交换机的介绍 Topics交换机能让消息只发送往绑定了指定routingkey的队列中去&#xff0c;不同于Direct交换机的是&#xff0c;Topics能把一个消息往多个不同的队列发送&#xff1b;Topics交换机的routingkey不能随意写&#xff0c;必须是一个单词列表&#xff0c;并以…...

解决PyInstaller打包selenium脚本时弹出driver终端窗口

解决PyInstaller打包selenium脚本时弹出driver终端窗口 找到service.py C:\Users\XXX\AppData\Roaming\Python\Python39\site-packages\selenium\webdriver\common\service.py添加creationflags 在第77行添加: creationflags134217728使用PyInstaller打包 pyinstaller -F -w -…...

基于卷积神经网络VGG的猫狗识别

&#xff01;有需要本项目的实验源码的可以私信博主&#xff01; 摘要&#xff1a;随着大数据时代的到来&#xff0c;深度学习、数据挖掘、图像处理等已经成为了一个热门研究方向。深度学习是一个复杂的机器学习算法&#xff0c;在语音和图像识别方面取得的效果&#xff0c;远远…...

mysql查询语句练习总结(涵盖所有sql语法)

最近在学习SQL嘛,所以各个地方找题目来练手,毕竟现在能离得开数据库么? Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,Tname) 教师表 问题&#xff1a; 1、查询“001”课程比“002”课程成绩高的所有学生的学号&#x…...

TypeScript 中 any、unknown、never 和 void 有什么区别?

一 unknown: 未知类型 unknown: 未知类型是typescript 3.0 中引入的新类型。 1.1 所有类型的字面量都可以分配给unknown类型 unknown未知类型&#xff0c;代表变量类型未知&#xff0c;也就是可能为任意类型&#xff0c;所以&#xff0c; 所有类型的字面量都可以分配给unkno…...

算法Day60 | 84.柱状图中最大的矩形,刷题总结

Day60 84.柱状图中最大的矩形刷题总结 84.柱状图中最大的矩形 题目链接&#xff1a;84.柱状图中最大的矩形 遍历每个元素&#xff0c;找到左右元素小于当前元素的&#xff0c;以左右元素间的区间&#xff08;左开右开区间&#xff09;所围成的面积中的最大值。 数组尾部加一个…...