MyBatis查询数据库

日升时奋斗,日落时自省 

目录

1、MyBatis

1.1、Mybatis配置

1.2、添加MyBatis框架支持

1.3、配置连接字符串和MyBatis

1.3.1、配置连接字符串

1.3.2、配置MyBatis中XML路径

2、业务代码

2.1、添加实体类

2.2、添加mapper接口

2.3、添加.xml文件

3、单元测试

3.1、单元测试优势

3.2、Spring Boot单元测试使用

4、MyBatis操作数据库

4.1、查询操作(单表)

4.1.1、有参查询

4.1.2、${}诱发SQL注入(传对象举例)

4.1.3、#{}解决${}带来的SQL注入(传对象举例)

4.1.4、like模糊查询

4.1.5、#{}和${}的区别

4.2、数据修改(单表)

4.3、数据删除(单表)

4.4、数据插入(单表)

4.4.1、插入操作(普通插入)

4.4.2、插入操作(设置主键)

4.5、多表查询

5、动态SQL使用

5.1、if标签

5.2、trim标签

5.3、where标签

5.4、set标签

5.5、foreach标签

如果是一路平缓的学习,那一定了解过JDBC,以及JDBC如何连接数据库,但是那时学习的数据库连接操作代码冗余太多,操作也比较复杂。

注:本篇内容有点长(选取自己需要的即可)

1、MyBatis

此处MyBatis查询数据库与Spring系列连起来,前面学习内容都不能进行保存,除了日志记录是保存文件中的,其他数据存储都是保存子内存中的,当下就通过MyBatis来连接数据库

MyBatis是一个优秀的基于Java的持久层框架,它支持定制化SQL、存储过程以及高级映射。 Mybatis通过XML或注解方式将要执行的各种statement(statement、preparedStatment、callableStatment)配置起来,并通过java对象和statement中的sql进行映射生成最终的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回

<1>自定义SQL:指开发者可以手动编写SQL语句并通过MyBatis框架执行的方式进行数据操作

<2>存储过程:预先保存在数据库中的一段程序,可以被多次调用,通常用于执行特定的复杂业务逻辑或数据操作

<3>高级映射:在SQL查询语句中加入关联查询的语句,并通过ResultMap将查询结果映射成Java对象

Mybatis去除了JDBC繁琐的代码(获取参数结果集等),Mybatis通过简单的XML文件或者注解来配置和映射原始类型,接口和java对象

1.1、Mybatis配置

配置Mybatis开发环境

使用Mybatis模式和语法操作数据库

MyBatis也是ORM框架,ORM(Object Relational Mapping)即位对象关系映射,在面向对象语言的编程中,将关系来型数据与对象建立起映射关系,进而自动的完成数据域对象的互相转换:

<1>将输入数据(即传入对象)+SQL映射成原生SQL

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

ORM把数据库映射为对象(规定性映射):

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

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

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

1.2、添加MyBatis框架支持

添加MyBatis框架支持分为两种情况:

<1>一种情况是对自己之前的Spring项目进行升级

<2>另一种情况是创建一个全新的MyBatis和Spring Boot的项目

下面开始创建一个项目并且开始附带上Mybatis需要的依赖:

创建Spring Boot项目 这里只展示引入依赖部分

后面的操作部分和Spring Boot项目创建操作相同

1.3、配置连接字符串和MyBatis

1.3.1、配置连接字符串

Spring Boot配置文件相关

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

这里给友友们提供数据库和其中表(但是没有数据,可以自己加两条)

我们这里使用.yml配置文件进行配置设置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

注:这里的数据库连接自己的数据库,因为我这里的数据库名称叫做 mycnblog ;

友友们填写的自己的数据库就可以了

 driver-class-name配置项:设置驱动程序的名称(是固定的,直接复制就行我们这里使用的是mysql的)

注:mysql的版本是有一定规定的,mysql5.x版本之前的使用“com.mysql.jdbc.Driver”这个配置
如果是高于5.x版本的使用“com.mysql.cj.jdbc.Driver”

1.3.2、配置MyBatis中XML路径

MyBatis的XML中保存是查询数据库的具体操作SQL,配置如下:

这里还会设置到 日志的打印级别,因为日志级别的Mybatis打印的日志级别是Debug级别的,但是Spring Boot 默认的打印级别是INFO,所以这里也会将日志级别设置较低

mybatis:
  mapper-locations: classpath:mybatis/*Mapper.xml
  # 设置 Mybatis 的 xml 保存路径
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 配置打印 MyBatis 执行的 SQL
  logging:
    level:
      com:
        example:
          demo: debug

 注意几个点:

2、业务代码

添加业务代码,有这么一系列流程来实现MyBatis查询用户功能,当前先走第一步写一个实体类

 2.1、添加实体类

这个实体类也不是随便加的,因为我们想用MyBatis,所以要遵循规定,MyBatis的映射关系

以下是我想要映射的数据库表单,所以java对象的实体类要与之一样

 这里创建entity层,该层存放实体类,

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

2.2、添加mapper接口

这里我们定义一个mapper层,用来写mapper接口(一定注意这里创建的是一个接口)

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

getAll()就是我们写的方法用来操控数据库的,但是暂时还没有写sql代码

注:mapper接口,上一定要写@Mapper注解

@Mapper:MyBatis会自动扫描所有的接口,然后根据接口定义创建相应的代理对象。这样我们就可以直接使用该接口来操作数据库,而无需手动编写SQL语句的实现

2.3、添加.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.mapper.UserMapper">

</mapper>

 此处就要开始写sql语句了,在mapper标签内写sql操作语句

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

id:写的是映射java接口路径下对应的方法名

resultType:指定查询结果集中每行数据的映射类型的属性,这里我们操作是UserEntity实体类,就写该类的路径就行

<select>标签内就开始写 sql 语句

接下来我们来写controller层操控数据库,前提需要调用service层的接口,所以还需要写一下service的接口

Service层:

@Service  //类注解 一定要记得 即是区分 也 是将注入spring容器
public class UserService {
    //将数据库类 进行注入
    @Autowired
    private UserMapper userMapper;
    
    public List<UserEntity> getAll(){
        return  userMapper.getAll();
    }
}

 controller层:

@Controller  //设置类注解
@RequestMapping("/user")  //设置路由
public class UserController {
    //service层对象注入
    @Autowired
    private UserService userService;
    //设置方法对应的路由
    @RequestMapping("/sayhi")
    public void sayhi(){
        userService.getAll();
    }
}

 访问路由结果:

现在开始就需要进行测试我们的代码成功了没有,但是直接写操作数据库的代码就很不方便,如果这里不是查询,是添加,修改,友友们是不是还得去数据库改回来,无意义的操作,所以针对这点产生了单元测试操作,避免对数据库的污染(就是不修改数据的同时也能知道,操作数据库的代码对不对)

3、单元测试

单元测试:用于测试程序中的最小可测试单元(代码中的一个函数或方法)。可以帮助开发人员及时打掉代码中的bug,提高代码质量,并减少在后期集成测试和系统测试中的错误

编写一小段代码,用于检测被测试代码的一个很小的,很明确(代码)功能是否正确,执行单元测试证明某段代码的执行结果是否符合我们的预期。通过则满足,不同过不满足

3.1、单元测试优势

<1>简单、直观、快速的测试某个功能是否正确

<2>使用单元测试可以帮我们打包的时候,发现问题,在打包之前就会告诉我们,所以单元测试就是必须的,否则打包不成功

<3>使用单元测试,在测试功能的时候,可以不污染连接的数据库,操作可以进行回滚

3.2、Spring Boot单元测试使用

Spring Boot针对单元测试有常用的框架如下:

  1. JUnit:JUnit是一个Java语言的单元测试框架,提供了一些用于编写测试用例的注解和断言工具,可以方便地创建测试用例并运行测试。

  2. Mockito:Mockito是一个用于Java的mock框架,用于模拟对象和行为,使得单元测试更加简单和可靠。

  3. Hamcrest:Hamcrest是一个Java语言的匹配器库,它提供了一些强大的断言语法,可以更加灵活地对测试结果进行检查。

  4. AssertJ:AssertJ是一个Java语言的断言库,提供了一些流畅的API,使得编写测试用例更加简单和易读。

  5. TestNG:TestNG是一个基于JUnit的测试框架,提供了一些更加高级和灵活的测试功能,例如测试套件、测试参数化等

在Spring boot项目的目录中是有对应的jar包的 

 剩下就开始操作,如何使用单元测试在

在mapper层写一个接口处理一个实体类(表)的sql操作进行测试

 测试选择项,这里当前只需要你勾选测试方法,其他的不要动,因为不需要用,点击OK就可以了

 测试代码:

@SpringBootTest   //这个注解不能忘了, 他是连接Spring boot的 用于测试使用,要不直接启动这里的 测试类直接就结束了
class UserMapperTest {
    //2、需要进行对象注入   为什么接口可以实例化???
    @Autowired
    private UserMapper userMapper;

    //1、生成代码如下
    @Test
    void getAll() {
        //3、测试打印该查询sql能不能顺利进行
        List<UserEntity> lists=userMapper.getAll();
        System.out.println(lists);
    }
}

该方法左边有一个绿色的三角,点击这里进行启动

 注:这里不需要页面的访问,什么也不需要,就能看见操作数据库代码是否正确

当然了如果需要严谨一点的话,可以加上断言

断言:用于判断测试结果是否符合预期的方法

 此处做以了解即可(大部分情况不用,以下是相关方法)

 这里说单元测试,为了我们后面的操作都使用单元测试来完成操作

4、MyBatis操作数据库

在操作之前,我们这里给友友们提供一个比较好用的插件,为了方便我们写sql代码

FIle->Settings->plugins

没有安装的友友们, 点击install即可,这里的效果下面在查询操作就给友友们展示 

4.1、查询操作(单表)

4.1.1、有参查询

这里涉及到一个注解@Param注解(该注解也能修改名称,也就是取别名),可以不使用注解直接传参但是不太稳妥,总是可能出问题的

原因:IDEA编译时采取了强制保持方法参数变量名

这就是但是即便是以上情况也要满足jdk使用8版本以上的或者jdk8,所以友友们还是加上注解

Java接口内写sql操作方法:

@Mapper
public interface UserMapper {
    List<UserEntity> getAll();
    //注解@Param    接收的名称为uid 
    UserEntity getUserId(@Param("uid") Integer id);
}

 .xml文件中写sql语句 id 就是我们要操作Java接口 中对应的的方法 

注意传参这里使用的是${} 这种形势,遵守规定即可,还有另外一种传参方式#{} ,这两个有所区别,后面给友友们介绍

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

代码都写好了,现在开始单元测试:(还是原来的操作,除了选择的方法不同以外其他没有什么区别)

在我们写操作数据库方法的Java接口处 单击右键  generate ->Test

 还是在刚刚创建测试类中 (一起都沿用之前有的,对象注入也适用刚刚写过的):

    @Test
    void getUserId() {
        UserEntity user=userMapper.getUserId(1);
        System.out.println(user);
    }

 运行结果:

 注:红色框框中显示的是我们写的sql语句,蓝色框框就是传参过程中,我们拿到值

 4.1.2、${}诱发SQL注入(传对象举例)

使用${}是很不安全的,容易叫人进行恶意拿取数据

SQL注入:是一种攻击技术,攻击者利用输入验证不严格的网站或应用程序中的漏洞将恶意SQL代码插入到查询中,从而绕过身份验证和控制访问数据的机制

这里给友友们演示一下:${}带来的问题

还是写一个login操作 登录需要涉及用户名和密码(还是在原来的UserMapper接口中写):

    UserEntity login(UserEntity user);

注:此时传递的是一个对象

对象映射到.xml文件中,属性怎么设置,Spring boot 已经给我们封装好了,直接使用属性就传参就行了

.xml文件 sql 语句 :

    <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 user=new UserEntity();
        user.setUsername(username);
        user.setPassword(password);
        UserEntity user1=userMapper.login(user);
        System.out.println(user1);
    }

 运行结果:

这里不能看出字符串少了引号,所以sql语句错了,那们就给给他添加上引号

 给sql语句添加上 单引号:

 这次就不会报错了:

 正是因为这样,字符串的拼接带来了SQL注入的问题,当前是没有拿到信息的,total是:0,这才是正确的,数据不对不应该拿到信息

以下是我数据库中的用户和密码:

 这里就开始展现SQL注入如何获取信息(只修改了密码输入)

 运行结果:

4.1.3、#{}解决${}带来的SQL注入(传对象举例)

解决${}的方法就是使用#{} ,一旦使用${}就需要格外注意SQL注入

把原来的$换成# 直接运行单元测试:

 另一种方式就需要在接收数据时,需要处理部分特殊情况,防止诱发SQL注入

当然了${} 也不是没有什么用处,#{}传递的是属性,不能传递sql中的关键字,${}能(凡是存在必有道理),这里试问:什么情况会涉及到传sql关键字呀,感觉基本没有什么情况是吧

上事例:

如果需要设置升降序

java接口内操作sql的方法:

    //sql 关键字 传递
    List<UserEntity> getUserByUserOrder(@Param("ord") String ord);

 .xml文件sql语句(这里只能使用${} ,#{}会报错):

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

添加单元测试 进行测试(传参直接传 sql 关键字 desc 倒序方便看到效果):

    @Test
    void getUserByUserName() {
        List<UserEntity> lists=userMapper.getUserByUserOrder("desc");
        for (UserEntity list:lists) {
            System.out.println(list);
        }
    }

单元测试结果(是可以做到,但是做到是做到了,但是还是要注意SQL注入,可以使用传一个值来判定升降即可,尽可能避免掉SQL注入):

注:后面的操作都会使用#{} 来接收属性值

4.1.4、like模糊查询

Java接口内操作sql方法:

模糊查询直接 需要 %字符串% 这个确实使用#{}是输入不出来的,虽然${}是可以进行字符串拼接操作的,但是还是不得不提防SQL注入:

所以这里就给友友们推荐一个方法 concat 

concat:有多少参数都可以进行拼接 ,下面开始操作一下:

Java接口内操作sql的方法:

    List<UserEntity> getListByName(@Param("username")String username);

.xml文件中的sql语句(以下是${}版的,不安全):

    <select id="getListByName" resultType="com.example.demo.entity.UserEntity">
        select * from userinfo where username
        like '%${username}%'
    </select>

 .xml文件中的sql语句(以下是concat方法版的,比${}安全):

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

 单元测试(因为这里是模糊查找所以只需要输入一部分即可):

    @Test
    void getListByName() {
        String username="无";
        List<UserEntity> list=userMapper.getListByName(username);
        //list.stream().forEach(System.out::println);  //stream是正则表达式的封装,可以进行直接打印
        //如果没有了解过stream 的 直接使用for循环即可
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }

 运行结果:

4.1.5、#{}和${}的区别

<1>功能不同:${} 是直接替换,而 #{}是占位符;

<2>使用场景不同:普通参数使用 #{},如果传递的是 SQL 命令或 SQL 关键字,需要使用 ${},但在使用前一定要做好安全验证;

<3>安全性:使用 ${} 存在安全问题,如 SQL 注入,而 #{} 则不存在安全问题。

4.2、数据修改(单表)

修改数据就没有那么那么复杂了,但是这里会新认识一个相关单元测试的注解@Transactional

Java接口内 sql 方法代码:

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

.xml文件sql语句 :

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

单元测试:

    @Test
    void updatePassword() {
        int result=userMapper.updatePassword(1,"123456","admin");
        System.out.println("修改行数:"+result);
    }

运行结果:

 此时要想不污染数据库需要添加一个注解@Transactional,才能进行回滚不影响数据库数据

 4.3、数据删除(单表)

因为修改和删除简单,所以先举例,容易接收,有了前面的地基,其实操作也很类似

这里就直接写一个删除的例子友友们就基本了解了:

Java接口内 代码:

    //删除用户信息
    int deleteById(@Param("uid") Integer id);

.xml文件sql 语句:

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

 单元测试:

    @Transactional   //该注解 会让操作的数据 进行回滚
    @Test
    void deleteById() {
        int result=userMapper.deleteById(6);
        System.out.println("删除影响的行数:"+result);
    }

运行结果(前面已经介绍了 回滚的注解,介绍就用起来):

 4.4、数据插入(单表)

普通的插入操作和前面的 删除和修改操作 很类似

4.4.1、插入操作(普通插入)

Java接口内操作sql的方法:

    //添加用户
    int addUser(UserEntity user);

.xml文件sql语句:

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

 单元测试(此处使用注解 进行回滚操作了):

    @Transactional
    @Test
    void addUser() {
        String username="黑无常";
        String password="123456";
        UserEntity input=new UserEntity();
        input.setUsername(username);
        input.setPassword(password);
        int result=userMapper.addUser(input);
        System.out.println("添加影响的行数:"+result);
    }

 运行结果:

4.4.2、插入操作(设置主键)

数据插入就与前面有所不同了,设置自动生成主键等操作

看代码来理解  

Java接口内写一个添加操作的方法:

    //插入操作 主键设置
    int addUserGetId(UserEntity user);

.xml文件中写插入的sql语句:

<!-- 此处设置 useGeneratedKeys为true 表示的是能设置:使用数据库自动生成的主键-->
<!--        keyProperty 设置参数就是 数据库对应   -->
    <insert id="addUserGetId" useGeneratedKeys="true" keyProperty="id">
        insert into userinfo(username,password) values(#{username},#{password});
    </insert>

注:涉及到两个新的参数一个是:useGeneratedKeys,另一个是keyProperty

useGeneratedKeys:是一个可选的配置属性,可以用于指示 MyBatis 是否应该使用数据库自动生成的主键。当 useGeneratedKeys 设置为 true 时,MyBatis 将使用 JDBC 的 getGeneratedKeys 方法来获取自动生成的主键(前提是你数据库创建表已经设置里主键),并将其设置回实体对象中

keyProperty:对应数据库主键字段

单元测试:

    @Test
    void addUserGetId() {
        String username="白无常";
        String password="123456";
        UserEntity input=new UserEntity();
        //设置新的 用户名和 密码
        input.setUsername(username);
        input.setPassword(password);
        //传递对象直接传,  Spring boot中的映射会进行获取其中属性 对应到xml文件中写sql语句
        int result=userMapper.addUserGetId(input);
        System.out.println("添加行数:"+result);
    }

这里就先不加@Transactional注解了,为了方便友友观察到

单元测试启动:然后看一下数据库,是否有新增 :

4.5、多表查询

这里我们就尝试操作多个表,这里创建一个文章表,但是我们还想知道这个文章对应的是哪个用户的

扩展的实体类:用户的名字字段

文章表的实体类:文章表字段信息

文章实体类:

@Data
public class ArticleInfo {
    private Integer id;
    private String title;
    private String content;
    private String createtime;
    private String updatetime;
    private Integer uid;
    private Integer rcount;
    private Integer state;
}

扩展表的需要的名称:

@Data
public class AritcleInfoVo extends ArticleInfo {
    //Data注解来自于 Lombok是不能直接 提供父类的toString
    //所以这里就需要 自己进行generate 快捷一个toString
    @Override
    public String toString() {
        return "AritcleInfoVo{" +
                "username='" + username + '\'' +
                "} " + super.toString();
    }
    private String username;
}

注:类虽然有继承父类,但是Lombok提供的注解不行,所以我们需要自己来写已给toString

这里toString怎么来的,给友友们简要操作一下:

 这里创建操作和原来的无异(接下来就以操作文章表进行):

 注:蓝鸟写 Java接口内操作sql的方法 ; 红鸟:.xml文件写sql语句

前置操作已经做完了,下面开始多表查询:

Java接口内 操作sql 的方法(通过id获取多表查询信息):

@Mapper
public interface AritcleMapper {
    List<AritcleInfoVo> getAll(@Param("uid")Integer uid);
}

.xml文件中的sql语句:

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

单元测试(获得uid = 1,也就是用户编号为1的文章 ):

@SpringBootTest
class AritcleMapperTest {
    @Autowired
    private AritcleMapper aritcleMapper;
    //测试多表查询
    @Test
    void getAll() {
        int uid=1;
        List<AritcleInfoVo> lists=aritcleMapper.getAll(uid);
        for(int i=0;i<lists.size();i++){
            System.out.println(lists.get(i));
        }
    }
}

运行结果:

注:为什么这里只说到多表的查询,是因为其他的多表操作的基本不太敢用,多表删除一条数据回影响很多条数据,修改也是类似;

5、动态SQL使用

5.1、if标签

为啥有个动态SQL呢,我们不是所有选择都是一定的

举个例子:就像我们去填写信息的时候(注册csdn账号),注册只填写部分必要信息,还有一些是可写可不写的,这部分怎么写入数据库,个数少了还好说,没种情况我都写一个方法来操作数据存入,个数多了咋办,像填写简历的信息的时候,那么多信息可选可不选,所有情况都要写吗??

答案:不需要

这里使用<if>标签来解决这个可选问题

我们这里使用userinfo用户表,用户和密码总是要传的嘛,头像可以不传

Java接口内 sql 操作方法:

    //if 标签
    int addUser2(UserEntity user);

.xml文件sql语句:

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

<if> 标签就是数据库某个字段必成可传可不传的

使用特点:<if>标签使用时一定需要设置test参数,否则会报红,test参数里面就写限制条件,photo就是我数据库中的图片字段,意为:如果图片为空的话,<if>标签就不满足,那就不执行标签内的字段

 单元测试(添加了@Transactional注解,不会污染数据库):

    @Transactional
    @Test
    void addUser2() {
        String username="白无常";
        String password="123456";
        UserEntity input=new UserEntity();
        input.setUsername(username);
        input.setPassword(password);
        int result=userMapper.addUser2(input);
        System.out.println("插入:"+result+"条");
    }

运行结果:

5.2、trim标签

这里我们更换以下顺序,为了突显效果像这样传参 username,password,photo

trim标签又是干什么的,if标签是让sql成为了动态的,但是不代表if标签一个人就能完成无错操作,

注意细节的友友们,会看到写if标签中除了password字段后面还跟了一个“逗号”,如果这个逗号

举个例子:

如果 username,password,photo 就以这个顺序为例,假如这三个属性都是可选可不选的,那要是photo没有传的话,就根据上面if标签来写

产生结果: usernam,password,    这样的sql能运行吗???我相信肯定是不能的

trim标签能去掉没有用的后缀或者前缀,所以这时在if标签外面写一个trim标签来控制后缀多余的“逗号”就可以解决问题(不要觉得这个问题是特殊问题,很常见)

trim标签有四个参数:

<1>prefix:表示整个语句块,以prefix的值作为前缀
<2>suffix:表示整个语句块,以suffix的值作为后缀
<3>prefixOverrides:表示整个语句块要去除掉的前缀
<4>suffixOverrides:表示整个语句块要去除掉的后缀

java接口内操作sql的方法(添加方法):

    //trim 标签
    int addUser3(UserEntity user);

.xml文件sql语句:

    <insert id="addUser3">
        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>

单元测试(我们这里只传递username和photo,最后一个参数的位置我们空下来):

    @Transactional
    @Test
    void addUser3() {
        String username="白无常";
        String photo="bind.jpg";
        UserEntity input=new UserEntity();
        input.setUsername(username);
        input.setPhoto(photo);
        int result=userMapper.addUser3(input);
        System.out.println("插入:"+result+"条");
    }

注:生成的sql应该是 insert into userinfo(username,photo,) values(?,?);

经过trim标签的处理:看以下运行结果:

 5.3、where标签

where标签又是来干什么的呢 主要避免什么都不输入的情况,你像你输入查询情况,他就默认全部给你看,查询再过滤掉你不看的

举个例子:

这里以文章表来说:  如果我想查询某几个信息

select * from articleinfo where id=#{id} and title like concat('%',#{title},'%');

如果此时没有输入id 和 title的话我们在.xml文件写的sql就会变成:

select * from articleinfo where 这就啥也不是了(下面开始演示where标签)

Java接口内操作sql的方法:

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

.xml文件的sql语句(where 还会清除if标签中前置and ,这是规定,遵守就行,所以我们尽量将and将在if标签内 要写的sql语句的前面):

    <select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.AritcleInfoVo">
        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>

单元测试(来看一下sql语句,以下测试三个结果):

    @Test
    void getListByIdOrTitle() {
        List<AritcleInfoVo> lists=aritcleMapper.getListByIdOrTitle(1,"Java");
        System.out.println(lists.size());
    }

运行结果:

 5.4、set标签

set 关键字 针对sql语句来想起来的一定是修改,这就是给修改使用的 有了前面的三个标签,这个标签的使用方法也是一致的(这里就写一个修改的sql语句操作)

Java接口内操作sql的方法(修改内容直接传一个对象,如果实体类中属性进行添加了也不会有什么影响):

    //set 标签
    int updateById(UserEntity user);

.xml文件的SQL语句:

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

set标签有一个特点,就是if标签内后置的“逗号”set是会根据mysql 语法进行去除的,就像where标签可以去掉if标签内前置的and一样(都是一个道理,当然这些都是规定)

单元测试(观察password后面的“逗号”去除):

这里我们传的是对象,给对象设置id 就是where后面指定要修改的id,其他设置都是修改内容

    @Transactional
    @Test
    void updateById() {
        String username="小刘";
        String password="admin";
        UserEntity input=new UserEntity();
        input.setId(3);
        input.setUsername(username);
        input.setPassword(password);
        int result=aritcleMapper.updateById(input);
        System.out.println("修改数:"+result);
    }

运行结果:

 注:这里只是告诉了友友们的使用特点,也不是说就只有这一种情况,这些标签可以混合使用

5.5、foreach标签

foreach可以设置五个参数:

<1>collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
<2>item:遍历时的每⼀个对象  (就相当于创建临时变量来接收一样)
<3>open:语句块开头的字符串
<4>close:语句块结束的字符串
<5>separator:每次遍历之间间隔的字符串

针对单表的多条数据删除也是比较常见,foreach也就是为了这样的操作(下面直接开始演示案例)

Java接口内操作sql方法:

    // 根据文章id集合批量删除文章 foreach 标签
    int delByIdList(List<Integer> idList);

.xml文件sql语句:

    <delete id="delByIdList">
        delete from articleinfo
        where id in
<!--        collection 参数就是java接口内 操作sql方法的 参数-->
        <foreach collection="idList" open="(" close=")" item="aid" separator=",">
            #{aid}
        </foreach>
    </delete>

单元测试(我这里刚好有3条数据所以只添加了3个id作为演示,可以回滚):

    @Transactional
    @Test
    void delByIdList() {
        List<Integer> lists=new ArrayList<>();
        lists.add(1);
        lists.add(2);
        lists.add(3);
        int result=aritcleMapper.delByIdList(lists);
        System.out.println("删除行数:"+result);
    }

运行结果:

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值