【Mybatis】动态SQL与留言板小项目


🎬 那我掉的头发算什么个人主页
🔥 个人专栏 : 《javaSE》《数据结构》《数据库》《javaEE》

⛺️待到苦尽甘来日


文章目录

动态SQL

标签

在注册用户时,有些属性是必填的,有些是不必须的,一般不强制要求的都有自己的默认值,这个时候:

如果传入的是空值,我们原先设置的默认值就会无意义,并且对于有些属性来说,null值本身就无意义。

这个时候就可以使用动态标签了,让非必填字段传入默认值而不是null。

咱们为了更好的演示,先来到数据库这里把所有的属性设置成可以为空。

java 复制代码
Integer insertUserByIf(UserInfo userInfo);
java 复制代码
<insert id="insertUserByIf">
        insert into user_info
            (username,password,age
        <if test="gender!=null">
            ,gender
        </if>
        )
        values
            (#{username},#{password},#{age}
        <if test="gender!=null">
        ,#{gender}
        </if>
        )
    </insert>
java 复制代码
@Test
    void insertUserByIf() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("username5");
        userInfo.setPassword("password5");
        userInfo.setAge(18);
        userInfo.setGender(1);
        userInfoMapperXml.insertUserByIf(userInfo);
    }

当传递了gender的值之后,数据库中属性会被设置成1,不传参则为0。

除了使用xml之外,我们也可以使用注解的方法实现动态SQL,只不过代码会很麻烦,大家看了就会明白的:

java 复制代码
@Insert("<script>" +
            "INSERT INTO user_info (username,`password`,age" +
            "<if test='gender!=null'>,gender</if>" + ")" +
            "VALUES(#{username},#{password},#{age}" +
            "<if test='gender!=null'>,#{gender}</if>" +")" +
            "</script>")
    Integer insertUserByCondition(UserInfo userInfo);

其实就是xml的代码加上 < script > 标签,而且还是用字符串拼接的,写的时候会很麻烦。。。

所以SQL注入这一块的代码还是xml的方式实现更加便捷。

< trim >标签

如果,我们传入的所有属性都是非必需属性的话。。。。。

java 复制代码
<insert id="insertUserByIf2">
        insert into user_info
        (
        <if test="username!=null">
            username
        </if>
        <if test="password!=null">
            ,password
        </if>
        <if test="age!=null">
            ,age
        </if>
        <if test="gender!=null">
            ,gender
        </if>
        )
        values
        (
        <if test="username!=null">
            #{username}
        </if>
        <if test="password!=null">
            ,#{password}
        </if>
        <if test="age!=null">
            ,#{age}
        </if>
        <if test="gender!=null">
            ,#{gender}
        </if>
        )
    </insert>

那就是上面的代码这种情况了。

这个代码看上去确实没啥问题,但是,如果传入的username属性为null的话,此时sql语句会变成:

java 复制代码
@Test
    void insertUserByIf2() {
        UserInfo userInfo = new UserInfo();
//        userInfo.setUsername("username6");
        userInfo.setPassword("password6");
        userInfo.setAge(18);
        userInfo.setGender(1);
        userInfoMapperXml.insertUserByIf2(userInfo);
    }

这个输出格式确实有点丑哈,咱们可以看到,这里多出了俩",",当第一个属性被去除,后面的逗号就多余了。这时候聪明的你肯定会说,那把逗号都放到后面不就可以了吗?这样的话username不传参确实没问题,但是最后一个属性不传参也会出现","遗留问题。。

这个时候就可以使用《trim》标签了!

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

java 复制代码
<insert id="insertUserByIf2">
        insert into user_info
        (
         <trim prefixOverrides=",">
             <if test="username!=null">
                 username
             </if>
             <if test="password!=null">
                 ,password
             </if>
             <if test="age!=null">
                 ,age
             </if>
             <if test="gender!=null">
                 ,gender
             </if>
             )
             values
             (
             <if test="username!=null">
                 #{username}
             </if>
             <if test="password!=null">
                 ,#{password}
             </if>
             <if test="age!=null">
                 ,#{age}
             </if>
             <if test="gender!=null">
                 ,#{gender}
             </if>
         </trim>
        
        )
    </insert>

把原来的代码放到标签里面,这时候就会自动识别多余的","并且删除啦!

注解的方式就不展示了大概是这样:

这样写不仅麻烦,而且字符串写错了是不会报错的哦😁

< where >标签

像京东的这种分类标签该如何实现呢?

当用户选了这个分类相当于where语句中就加上了这个筛选条件,不选这个分类就相当于去除了,如果啥都不选就是展示所有商品。

就比如咱们这个代码,查询条件也是循序渐进的。

java 复制代码
<select id="selectByIf" resultType="com.hbu.springmybatisdemo.model.UserInfo">
        select * from user_info 
        where
        <if test="username!=null">
            username = #{username}
        </if>
        <if test="password!=null">
            and `password` = 'password5'
        </if>
        <if test="username!=null">
            and gender = 1
        </if>
    </select>

这个代码相信大家一眼就能看出破绽,第一个参数不传,and会多余出来。没错,这个时候使用《trim》就可以解决了。

但是,如果是一个参数都不传的场景下,此时多出一个"where"怎么办呢???

这个时候我们可以使用< where>标签!

java 复制代码
<select id="selectByIf" resultType="com.hbu.springmybatisdemo.model.UserInfo">
        select * from user_info 
        <where>
            <if test="username!=null">
                username = #{username}
            </if>
            <if test="password!=null">
                and `password` = 'password5'
            </if>
            <if test="username!=null">
                and gender = 1
            </if>
        </where>>
    </select>

< where> 只会在子元素有内容的情况下才插⼊where⼦句,⽽且会自动去除子句的开头的AND或OR或","

也就是说它还包含了< trim>标签的功能!

< set>标签

set关键字是更新操作特有的,至于应用场景,我就直接上代码吧:

java 复制代码
<update id="updateByIf">
        update user_info 
        <set>
            <if test="username!=null">
                username = #{username},
            </if>
            <if test="password!=null">
                password = #{password},
            </if>
            <if test="age!=null">
                age = #{age}
            </if>
        </set>                         
        where id = #{id}
        
    </update>

需要注意的是,这里我们的where语句不可以用where标签,因为这是一个必传的字段!

假如忘记传where,这里就会把所有的数据都修改!造成严重的后果。

改完之后数据库中也确实体现出来了。

< foreach>标签

当涉及到集合的传参时,我们没法直接把数据传过去,这个时候就需要借助< foreach>标签了。


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

java 复制代码
<select id="selectByIn" resultType="com.hbu.springmybatisdemo.model.UserInfo">
        select * from user_info where id IN
        <foreach collection="ids" open="(" close=")" separator="," item="id">
            #{id}
        </foreach>
    </select>

第二个删除的代码也很简单了:

java 复制代码
<delete id="deleteByIn">
        delete from user_info where
        <foreach collection="ids" item="id" separator="or">
            id = #{id}
        </foreach>
    </delete>

< include>标签

问题分析:在xml中配置的SQL,很多时候代码会存在重复的片段,也就是存在一些冗余的代码。我们可以对代码片段进行抽取,通过< sql>标签封装起来,然后通过< include>标签进行引用。

java 复制代码
<sql id="selectAll">
        select * from user_info
    </sql>
    <select id="selectAll" resultType="com.hbu.springmybatisdemo.model.UserInfo" resultMap="selectMap">
        <include refid="selectAll"> </include>
    </select>
java 复制代码
<select id="selectAllById" resultType="com.hbu.springmybatisdemo.model.UserInfo">
         <include refid="selectAll"> </include> where id = ${id}
    </select>

注意,我们说的是代码片段,零散的代码块也是可以封装起来的哦!

实战演练:留言板(连接数据库版)

咱们在刚开始做留言板小项目的时候曾经遇到一个问题:

发布过的留言没法保存,只能使用一个集合来存储在内存中,只要程序一重启,发布过的留言也没了。

但是,咱们学了Mybatis就可以与数据库连接,把这些消息存储在数据库中,实现持久化保存信息了!

model层

java 复制代码
package com.bite.spring.demo.model;

import lombok.Data;

@Data
public class MessageInfo {
    private String from;
    private String to;
    private String message;
}

存储留言的属性

Controller层

负责前后端交互

java 复制代码
package com.bite.spring.demo.Controller;

import com.bite.spring.demo.model.MessageInfo;
import com.bite.spring.demo.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/message")
public class MessageController {

    @Autowired
    private MessageService messageService;

    @PostMapping(value = "/publish")
//    @PostMapping(value = "/publish", produces = "application/json")
    public String publish(@RequestBody MessageInfo messageInfo){
        System.out.println("前端传递的参数:" + messageInfo.getFrom() + "," + messageInfo.getTo() + "," + messageInfo.getMessage());
        if (!StringUtils.hasLength(messageInfo.getFrom())
                || !StringUtils.hasLength(messageInfo.getTo())
                || !StringUtils.hasLength(messageInfo.getMessage())) {
            return "{\"ok\": 0}";
        }
        //存储留言
        messageService.insertInfo(messageInfo);
//        messageInfoList.add(messageInfo);
        return "{\"ok\": 1}";
    }

    @GetMapping("/getList")
    public List<MessageInfo> getList(){
        List<MessageInfo> messageInfoList = messageService.getList();
        return messageInfoList;
    }

}

Service层

java 复制代码
package com.bite.spring.demo.service;

import com.bite.spring.demo.mapper.MessageMapper;
import com.bite.spring.demo.model.MessageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MessageService {
    @Autowired
    private MessageMapper messageMapper;

    public List<MessageInfo> getList(){
        return messageMapper.selectAll();
    }

    public void insertInfo(MessageInfo messageInfo) {
        System.out.println("前端传递的参数:" + messageInfo.getFrom() + "," + messageInfo.getTo() + "," + messageInfo.getMessage());
        messageMapper.insertInfo(messageInfo);
    }
}

Mapper层

封装数据库操作接口

java 复制代码
package com.bite.spring.demo.mapper;

import com.bite.spring.demo.model.MessageInfo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface MessageMapper {

    @Select("select * from message_info")
    List<MessageInfo> selectAll();

    @Insert("insert into message_info (`from`,`to`,message ) values (#{from},#{to},#{message}) ")
    Integer insertInfo(MessageInfo messageInfo);
}

前端代码

java 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>留言板</title>
    <style>
        .container {
            width: 350px;
            height: 300px;
            margin: 0 auto;
            /* border: 1px black solid; */
            text-align: center;
        }

        .grey {
            color: grey;
        }

        .container .row {
            width: 350px;
            height: 40px;

            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .container .row input {
            width: 260px;
            height: 30px;
        }

        #submit {
            width: 350px;
            height: 40px;
            background-color: #f8c203;
            color: white;
            border: none;
            margin: 10px;
            border-radius: 5px;
            font-size: 20px;
        }
    </style>
</head>

<body>
<div class="container">
    <h1>留言板</h1>
    <p class="grey">输入后点击提交, 会将信息显示下方空白处</p>
    <div class="row">
        <span>谁:</span> <input type="text" name="" id="from">
    </div>
    <div class="row">
        <span>对谁:</span> <input type="text" name="" id="to">
    </div>
    <div class="row">
        <span>说什么:</span> <input type="text" name="" id="say">
    </div>
    <input type="button" value="提交" id="submit" onclick="submit()">
    <!-- <div>A 对 B 说: hello</div> -->
</div>

<script src="jquery-3.7.1.min.js"></script>
<script>
    load();

    function load() {
        $.ajax({
            type: "get",
            url: "/message/getList",
            success: function (messages) {
                if (messages != null && messages.length > 0) {
                    var finalHtml = "";
                    for (var m of messages) {
                        finalHtml += "<div>" + m.from + "对" + m.to + "说:" + m.message + "</div>";
                    }
                    $(".container").append(finalHtml);
                }

            }
        });
    }

    function submit() {
        //1. 获取留言的内容
        var from = $('#from').val();
        var to = $('#to').val();
        var say = $('#say').val();
        if (from == '' || to == '' || say == '') {
            return;
        }
        var data = {
            from: from,
            to: to,
            message: say
        };

        $.ajax({
            type: "post",
            url: "/message/publish",
            contentType: "application/json",
            data: JSON.stringify(data),
            success: function (result) {

                var jsonObj = JSON.parse(result);

                if (jsonObj.ok == 1) {
                    //成功
                    //2. 构造节点
                    var divE = "<div>" + from + "对" + to + "说:" + say + "</div>";
                    //3. 把节点添加到页面上
                    $(".container").append(divE);

                    //4. 清空输入框的值
                    $('#from').val("");
                    $('#to').val("");
                    $('#say').val("");
                } else {
                    //失败
                    alert("留言发布失败");
                }
            }

        });



    }

</script>
</body>

</html>

配置信息

java 复制代码
spring:
  application:
    name: springboot-demo
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: hjk050723
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration: # ???? MyBatis??
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true #????????

项目演示

刚进入网页,会直接显示getList接口的查询结果,把之前所有的留言都加载出来,并且刷新之后留言不会消失。


输入数据之后,添加的一条留言确实添加成功了。
以上就是本篇博客全部内容!

相关推荐
2501_941982052 小时前
从孤岛到闭环:如何将企微 RPA 自动化能力无缝接入业务工作流?
数据库
ALex_zry2 小时前
Redis Cluster 故障转移与高可用实践
数据库·redis·wpf
玄〤2 小时前
SpringCloud微服务用户身份拦截器配置详细解决方案(黑马商城)(springcloud微服务课day6)
spring·spring cloud·微服务
Re.不晚2 小时前
Redis入门--基础语法大全
数据库·redis·bootstrap
有诺千金2 小时前
SpringBoot3的前后端分离架构中使用SpringSecurity的思路
spring boot·架构
Warren982 小时前
一次文件上传异常的踩坑、定位与修复复盘(Spring Boot + 接口测试)
java·开发语言·spring boot·笔记·后端·python·面试
indexsunny2 小时前
互联网大厂Java面试实录:Spring Boot微服务与Kafka消息队列实战解析
java·spring boot·微服务·面试·kafka·电商·技术解析
pp起床2 小时前
【苍穹外卖】Day05 Redis快速入门
数据库·redis·缓存
一灰灰blog2 小时前
Jar包会自己消失?Excel会“记忆“数据?我遇到了两个灵异bug
java·spring boot·bug·excel