【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接口的查询结果,把之前所有的留言都加载出来,并且刷新之后留言不会消失。


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

相关推荐
oddsand118 分钟前
Redis网络模型
java·数据库·redis
皮卡祺q22 分钟前
【redies0-导论】分布式系统的演进-引进redis原因
java·数据库·redis
Don.TIk33 分钟前
ChapterOne-搭建项目骨架
java·spring·spring cloud·mybatis
南极企鹅39 分钟前
事务&@Transactional注解
java·数据库·spring·oracle·mybatis
UrSpecial43 分钟前
Redis与多线程
数据库·redis·缓存
bqq198610261 小时前
MySQL 8与MySQL 5.7的主要区别
数据库·mysql
happymaker06261 小时前
SpringBoot学习日记——DAY02(SpringBoot整合Swagger3)
java·spring boot·学习
chushiyunen1 小时前
r树索引、mysql对r树的支持
数据库·mysql
会编程的土豆1 小时前
Redis Sorted Set(有序集合)详解
数据库·redis·bootstrap
未若君雅裁1 小时前
Spring Boot 自动配置原理与常用注解
java·spring boot·后端