SpringBoot03-Mybatis框架入门

六、Mybatis

6.1 之前的 JDBC 的使用缺点?

  • 大量的代码重复
    • 加载驱动,获得连接,获得执行语句,执行 sql, 关闭连接
  • 手动加载驱动,创建连接 (Connection), 关连接
  • 封装数据麻烦 (ORM)
  • 效率不高 (没有缓存)

6.2 Mybatis 的介绍

官网: mybatis -- MyBatis 3 | Introduction

MyBatis 本是 apache 的一个开源项目 iBatis ,2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为**MyBatis** 。2013 年 11 月迁移到Github

iBATIS 一词来源于 "internet" 和 "abatis" 的组合,是一个基于 Java 的持久层 (Dao)框架。用于操作数据库。


MyBatis 是一款优秀的持久层框架,它支持定制化 SQL 、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集 。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs (Plain Ordinary Java Object, 普通的 Java 对象) 映射成数据库中的记录。且有缓存机制,可以提高查询效率。


Mybatis 是一个**半ORM框架** ,可以消除 JDBC 的代码和步骤,让开发者**只关注SQL**本身。

  • ORM 是对象关系映射,是指数据库表和 java 实体类一一对应.
  • 半 ORM 框架,还是需要写 SQL, 由框架帮你完成映射
  • 完全 ORM 框架,连 SQL 都不需要写,只需要遵循 ORM 的要求,就会自动生成 SQL 完成映射 (Hibernate,JPA,MybatisPlus 等)

6.3 xml 方式整合 Mybatis [重点]

xml 方式在编写复杂 SQL 时,更适合

6.3.1 环境

mybatis 和 druid 的 springboot 环境下的依赖 (别忘了 web 依赖)

xml

XML 复制代码
<!-- 小辣椒 -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>
<!-- mysql驱动 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.47</version>
</dependency>
<!-- druid 数据库连接池 -->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.1.10</version>
</dependency>
<!-- mybatis -->
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>1.3.2</version>
</dependency>

准备数据库

sql

sql 复制代码
CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',
  `username` varchar(10) DEFAULT NULL COMMENT '用户名',
  `password` varchar(255) DEFAULT NULL COMMENT '密码',
  `phone` varchar(11) DEFAULT NULL COMMENT '手机号',
  `create_time` date DEFAULT NULL COMMENT '注册时间',
  `money` double(10,2) DEFAULT NULL COMMENT '账户余额',
  `sex` int(1) DEFAULT NULL COMMENT '性别 1男2女',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8

实体类

java 复制代码
@Data
public class User {
    private int id;
    private String username;
    private String password;
    private String phone;
    private Date createTime;
    private double money;
    private int sex;
}

6.3.2 编写接口和映射文件

接口就是我们之前的 Dao 层接口,Mybatis 习惯叫做 Mapper, 所以先创建 mapper 包,然后再在其中创建接口文件

java 复制代码
public interface UserMapper {
    User findUserById(int id);
}

以前是写接口的实现类,现在 mybatis 的接口实现功能由 xml 文件来实现了在 resources / 下创建 m

XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace名称空间,根据接口的全限定名,来关联接口和xml文件-->
<mapper namespace="com.qf.day34mybatis.mapper.UserMapper">
    <!--前面接口中定义的方法,这里要写标签,然后写sql-->
    <!--根据需求,写不同的标签,
        查询 <select>
        更新 <update>
        删除 <delete>
        插入 <insert>
    -->
    <!--根据id查询用户-->
    <!--
        id: 是前面接口的方法名
        resultType: 结果类型,即语句的返回值类型,查询的结果会封装到这个类型的对象中
    -->
    <!--resultType,是要封装的类型的路径,比较长,如果配置type-aliases-package,可以省略包名-->
    <select id="getUserById" resultType="User">
        <!--原来?的地方,现在mybatis写的#{},内部写变量名-->
        select * from tb_user where id = #{id}
    </select>
</mapper>

6.3.3 yml 文件

SpringBoot 项目,默认配置文件类型是 properties 改成后缀为 yml 的文件

yaml

XML 复制代码
server:
  port: 8080
# 连接数据库的信息
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mydb5?serverTimezone=UTC&useSSL=false&characterEncoding=utf8&useUnicode=true
    username: root
    password: root
    # 数据库连接池
    type: com.alibaba.druid.pool.DruidDataSource
# mybatis配置
mybatis:
  # 扫描映射文件
  mapper-locations: classpath:mapper/*.xml
  # 配置别名扫描的包
  type-aliases-package: com.example.entity
  configuration:
    # 开启驼峰映射配置
    map-underscore-to-camel-case: true
# 打印执行过程的sql信息
logging:
  level:
    com.com.example.dao: DEBUG

6.3.4 扫描 mapper

主类扫描 mapper

java 复制代码
@SpringBootApplication
@MapperScan("com.qf.mapper") // 扫描mapper接口,创建代理对象
public class TestSpringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestSpringbootApplication.class, args);
    }
}

6.3.5 测试

创建 controller,service , 按以前三层架构,依次调用即可// Controller

java 复制代码
package com.qf.day34mybatis.controller;

import com.qf.day34mybatis.entity.User;
import com.qf.day34mybatis.service.UserService;
import com.qf.day34mybatis.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    // 根据id查询用户
    @GetMapping("/{id}")
    public R getUserById(@PathVariable Integer id) {
        User user = userService.getUserById(id);
        if (user == null) {
            return R.fail("用户不存在");
        }
        return R.ok(user);
    }
}

// 业务层 (接口 + 实现类)

java 复制代码
public interface UserService {
    User getUserById(Integer id);
}

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserById(Integer id) {
        return userMapper.getUserById(id);
    }
}

启动项目,发出请求localhost:8080/user/1即可


完整项目结构

6.3.6 接口工具测试

idea 下载 Cool Request 插件

6.4 CRUD [核心]

6.4.1 查询

6.4.1.1 单条件查询

详情看入门演示【特别注意:ORM 时字段名要和实体类属性一致,否则封装失败】

对象关系映射(Object Relational Mapping)

6.4.1.2 查询全部

设计查询接口方法

java 复制代码
public interface UserMapper {
    User findUserById(int id);
    List<User> findAll();
}

映射文件xml

XML 复制代码
<!-- 一个标签,就是一个SQL执行的语句 -->
<!-- 【注意】虽然查询返回集合,但是返回类型此处还要写集合中存储的类型 -->
<!-- 【或者这样理解】虽然返回集合,此处定义的是查询返回要封装的实体类类型 -->
<select id="findAll" resultType="User">
  select * from tb_user
</select>

一样的设计 Controller,Service 调用即可,启动项目测试

java 复制代码
// Server
public interface UserServer {
    List<User> findAll();

}

// ServerImpl
@Service
public class UserServerImpl implements UserServer {
    @Autowired
    private UserDao userDao;

    public List<User> findAll(){
        return (List<User>) userDao.findAll();
    }
}

// Controller
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserServer userServer;

    @RequestMapping("/findAll")
    public List<User> findAll(){
        return userServer.findAll();
    }
}
6.4.1.3 多参数查询

需求: 通过用户名和密码查询接口方法

java 复制代码
public interface UserMapper {
    User findUserByLogin(String username,String password);
}

映射文件xml

XML 复制代码
<select id="findUserByLogin" resultType="com.qf.model.User">
  <!-- 默认是不支持传多个参数,传入多个参数时,需要如下操作 -->
  <!--
  方案1: #{}内按顺序写param1,param2,....或者agr0,arg1
  但是此种方式不建议
  -->
  select * from tb_user 
  where username = #{param1} and password = #{param2}
</select>

(解决方案 2)(推荐) 接口方法(参数加注解)

java 复制代码
public interface UserMapper {
    User findUserByLogin(
        @Param("username") String username, 
        @Param("password") String password);
}

映射文件xml

XML 复制代码
<select id="findUserByLogin" resultType="User">
  <!-- 默认是不支持传多个参数,传入多个参数时,需要如下操作(2选1) -->
  <!--
  方案2: 1)在接口方法参数前添加注解@Param并设置注解参数
  2)在#{}内写注解中的值
  -->
  select * from tb_user 
  where username = #{username} and password = #{password}
</select>
6.4.1.4 Map 参数查询

需求:查询时,就要传递分页数据,又要传递模糊查询关键词,此时就可以使用 Map 来封装参数 (即请求中参数有点多....)接口方法

java 复制代码
public interface UserMapper {
    // Mapper接口传递map参数
    List<User> search(HashMap<String, String> map);
}

映射文件xml

XML 复制代码
<!--接口虽然返回的List,但是此处resultType写的是User-->
<!--此处是模拟搜索,先固定查询条件(查询用户名和密码都包含指定字符串的用户)-->
<select id="search" resultType="User">
    select * from tb_user
    where username like '%${username}%' and password like '%${password}%'
    <!--传递是map,${}或者#{} 写的是map的key-->
</select>

强调:前面使用 map 传递参数,${} #{} 内部需要根据 map 的 key 来取值:::

  1. #{} vs ${} 的区别:
    • #{}:预编译参数,会自动转义 ,能防止 SQL 注入,推荐使用 (需配合 CONCAT 函数拼接通配符)。
    • ${}:直接字符串替换有 SQL 注入风险,仅适用于确定无注入风险的场景(比如固定参数)。
java 复制代码
    // 搜索,模拟传递Map参数
    @GetMapping("/search")
    public R search(@RequestParam HashMap<String,String> map) {
        List<User> list = userService.search(map);
        return R.ok(list);
    }
6.4.1.5 对象参数

比如登录,前端传递 (用户名 + 密码)(手机号 + 验证码) 等等

  • 可以会传递多个参数,上面讲过可以在 Mapper 接口的方法中设计注解来解决或者使用 Map 封装数据
  • 但是,常见的方案是用对象接收前端数据,向业务层 / 持久层传递对象!接口方法
java 复制代码
public interface UserMapper {
     User loginV2(User user);
}

映射文件

XML 复制代码
    <select id="loginV2" resultType="com.qf.day34mybatis.entity.User">
        select * from tb_user
        where username = #{username} and password = #{password}
        <!--#{}是对象的属性名-->
    </select>

controller

java 复制代码
/**登录,使用对象接收
 * 前端发送json格式数据
 */
@PostMapping("/loginv2")
public R loginV2(@RequestBody User user){
    User user2 = userService.loginV2(user);
    if (user2 == null) {
        return R.fail("登录失败");
    }
    return R.ok(user2);
}

6.4.2 增加

需求:如下场景

前端使用 JSON 发送,后端使用对象接收还得使用 @RequestBody 配合
controller

java 复制代码
    /**
     * 添加用户
     * 添加/更新/登录 发post请求
     * 前端的数据格式是json
     */
    @PostMapping("/add")
    public R addUser(@RequestBody User user) {
        System.out.println("UserController.addUser收到数据,user"+user);
        // 调用业务层
        // 增删改,可以不要返回值
        // 也可以要返回值---> int / boolean
        userService.addUser(user);
        return R.ok();
    }

service

java 复制代码
public interface UserService {
    void addUser(User user);
}

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public void addUser(User user) {
        userMapper.addUser(user);
    }
}

Mapper 接口方法

java 复制代码
public interface UserMapper {
    void addUser(User user);
}

Mapper 映射文件

xml

XML 复制代码
    <!--插入,增删改标签内不需要定义返回值类型,但是想返回只需要接口的方法定义返回值类int boolean-->
    <insert id="addUser">
        insert into tb_user
        (username , password,phone,create_time,money,sex)
        values (#{username},#{password},#{phone},#{createTime},#{money},#{sex})
    </insert>

测试

6.4.3 修改

6.4.3 修改

需求场景:如下暂定:需求就是只要是传递过来的数据都会修改 (全量更新)

java 复制代码
// 更新
@PostMapping("/edit")
public R editUser(@RequestBody User user){
    userService.updateById(user);
    return R.ok();
}

service....接口方法

java 复制代码
public interface UserMapper {
    void updateById(User user); // 修改方法的参数是对象
}

映射文件

xml

XML 复制代码
<update id="updateById">
  <!-- 对象参数,#{}内属性名 -->
  update tb_user set username=#{username},password=#{password},
  phone= #{phone},create_time=#{createTime},money=#{money},sex=#{sex}
  where id = #{id}
</update>

测试

6.4.4 删除 (自学)

需求:场景如下

在前端,设置删除按钮,点击发出请求,请求中携带这条数据的 id
接口方法

java 复制代码
int deleteById (int id);

映射文件 xml

XML 复制代码
<delete id="deleteById">
  delete from tb_user where id = #{id}
</delete>

6.5 动态 SQL【重要!!!】

帮助我们拼接 SQL

MyBatis 动态 SQL 是一个模板适配所有查询场景 ,替代上面 4 条原生 SQL,核心靠 <where><if> 标签实现「条件动态拼接」

XML 复制代码
// 所有查询场景
select * from tb_user where username = ? and sex = ?
select * from tb_user
select * from tb_user where username= ?
select * from tb_user where sex = ?

// 该模板会根据传入的参数(用户名 / 性别)自动拼接条件,适配所有查询场景
<select id="findUserByCondition" resultType="User">
    select * from tb_user
    <where>
        <if test="username != null and username != ''">
            and username = #{username} <!-- 建议用#{参数名},比?更直观,MyBatis自动转? -->
        </if>
        <if test="sex != null and sex != ''">
            and sex = #{sex}
        </if>
    </where>
</select>

常见的动态 SQL 语法

  • sql 片段
  • where+if
  • set+if
  • foreach
  • trim
  • choose-when-otherwise

6.5.1 sql 片段

抽取重复 sql 语句

使用<sql id="">标签定义重复的 sql 语句

在下方的 sql 语句中使用 **<include refid="">**将其引入

6.5.2 where+if [重点]

场景:搜索

测试

Controller

java 复制代码
// 搜索2,模拟传递Map参数
@GetMapping("/search2")
public R searchV2(@RequestParam HashMap<String,String> map) {
    List<User> list = userService.searchV2(map);
    return R.ok(list);
}

service ...

java 复制代码
public interface UserService {
    List<User> searchV2(HashMap<String, String> map);
}

@Service
public class UserServiceImpl implements UserService {

    @Override
    public List<User> searchV2(HashMap<String, String> map) {
        return userMapper.searchV2(map);
    }
}

mapper

java 复制代码
public interface UserMapper {
    List<User> searchV2(HashMap<String, String> map);
}

模拟的效果是

  • 根据用户名,密码,手机号 模糊搜索
  • 根据注册时间 范围搜索

xml

XML 复制代码
<!--
    sql片段,id唯一名字
-->
<sql id="selectUserField">
    select
        id,username , password,phone,create_time,money,sex
    from tb_user
</sql>

<!--模糊搜索,演示动态sql where + if-->
<select id="searchV2" resultType="User">
  <include refid="selectUserField"/>
  <!--where标签,如果下方有条件,会自动产出where关键词,如果没有任何条件,where就消失-->
  <!--where会自动去掉多于的and-->
  <where>
    <!--取出map的键所对应值,判断有无数据-->
    <if test="username != null and username != ''">
      and username like '%${username}%'
    </if>
    <if test="phone != null and phone != ''">
      and phone like '%${phone}%'
    </if>
    <if test="password != null and password != ''">
      and password like '%${password}%'
    </if>
    <if test="beginTime != null">
      <!-- 大于>运算不能使用,需要写成 &gt;-->
      and create_time &gt; #{beginTime}
    </if>
    <if test="endTime != null">
      <!--小于< 运算,需要写成 &lt;-->
      and create_time &lt; #{endTime}
    </if>
  </where>
</select>

特别注意: xml 文件中,

大于 > 运算不能使用,需要写成 &gt;

小于 < 运算,需要写成**&lt;**

6.5.3 set+if

场景:动态更新 (部分更新)

之前更新是全量更新,只要没有传递数据,即认为要置为 null

其实我们认为,不传递数据是不修改,保持原样!
controller

java 复制代码
    /**
     * 更新2 动态更新,即没有传递的数据,即不更新保持原样
     */
    @PostMapping("/edit2")
    public R editUser2(@RequestBody User user){
        userService.updateById2(user);
        return R.ok();
    }

动态更新 mapper xml

传入参数情况 最终拼接的 SQL(简化版) 效果说明
仅传 id=1、username="zhangsan" update tb_user set username = ? where id = ? 只更新用户名,其他字段不变
传 id=1、password="123456"、sex=1 update tb_user set password = ?, sex = ? where id = ? 只更新密码和性别
传 id=1、所有字段都有值 update tb_user set username=?, password=?, phone=?, create_time=?, sex=?, money=? where id=? 所有字段都更新,无多余逗号
仅传 id=1、无其他字段 拼接为 update tb_user where id = ?(语法错误)
XML 复制代码
   <!--动态更新-->
    <update id="updateById2">
        update tb_user
        <!--set标签,  1)会产生set关键词, 2) 取出多于最后一个,逗号-->
        <set>
            <if test="username != null and username != ''">
                username = #{username},
            </if>
            <if test="password != null and password != ''">
                password = #{password},
            </if>
            <if test="phone != null and phone != ''">
                phone = #{phone},
            </if>
            <!--createTime是Date类型,不能和 字符串'' 判断-->
            <if test="createTime != null">
                create_time = #{createTime},
            </if>
            <!--sex是Integer类型,不能和 字符串'' 判断-->
            <if test="sex != null">
                sex = #{sex},
            </if>
            <if test="money != null">
                money = #{money},
            </if>
        </set>
        where id = #{id}
    </update>

6.5.4 foreach

场景

  • 批量删除
  • delete from tb_user where id in (1,2,3);
  • 批量插入 (自学)

测试

Controller

java 复制代码
// 批量删除
@GetMapping("/batch/del")
public R batchDel(@RequestParam List<Integer> ids) {
    userService.delBatch(ids);
    return R.ok();
}

service.....

批量删除

--> mapper xml

XML 复制代码
<!--批量删除-->
<delete id="delBatch">
  delete from tb_user where id in
  <!--collection是要遍历的集合,此处必须叫list
  item 就是遍历得到的数据 -->
  <foreach collection="list" item="id" open="(" separator="," close=")">
    #{id}
  </foreach>
</delete>

6.5.6 动态插入

MyBatis 实现 tb_user 表的动态插入功能,

核心价值是:根据传入 User 对象的非空字段,动态拼接插入的「字段列表」和「值列表」,只插入有值的字段,替代固定字段的原生插入 SQL

(如**insert into tb_user (username, sex) values(#{username}, #{sex})),**

适配多场景插入需求。

标签:<trim>(通用动态拼接标签)

<trim> 是 MyBatis 最灵活的动态标签,通过 3 个属性解决拼接语法问题,这里用了两个 <trim> 分别处理「字段列表」和「值列表」:

属性 作用 示例(第一个 <trim>
prefix 给拼接后的片段加「前缀」 prefix="(" → 字段列表开头加左括号 (
suffix 给拼接后的片段加「后缀」 suffix=")" → 字段列表结尾加右括号 )
suffixOverrides 剔除片段最后多余的指定字符(这里是 , 自动删掉最后一个字段后的逗号,避免 (username, sex,) 语法错误
XML 复制代码
insert into tb_user (username, sex) values(#{username}, #{sex})

<insert id="insertUser" parameterType="com.example.entity.User">
    INSERT INTO tb_user
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="username != null and username != ''">
            username,
        </if>
        <if test="sex != null and sex != ''">
            sex,
        </if>
        <if test="age != null">
            age,
        </if>
        <if test="email != null and email != ''">
            email,
        </if>
        <if test="phone != null and phone != ''">
            phone,
        </if>
        <if test="createTime != null">
            create_time,
        </if>
    </trim>
    <trim prefix="VALUES (" suffix=")" suffixOverrides=",">
        <if test="username != null and username != ''">
            #{username},
        </if>
        <if test="sex != null and sex != ''">
            #{sex},
        </if>
        <if test="age != null">
            #{age},
        </if>
        <if test="email != null and email != ''">
            #{email},
        </if>
        <if test="phone != null and phone != ''">
            #{phone},
        </if>
        <if test="createTime != null">
            #{createTime},
        </if>
    </trim>
</insert>

xml

XML 复制代码
<!-- 1. 定义插入语句,id 为方法名,parameterType 指定参数类型 -->
<insert id="insertUser" parameterType="com.example.entity.User">
    
    <!-- 2. 固定的 INSERT INTO 表名部分 -->
    INSERT INTO tb_user
    
    <!-- 3. 第一个 <trim> 标签:处理字段列表 -->
    <!-- 
        prefix="("    :在内容前添加左括号
        suffix=")"    :在内容后添加右括号  
        suffixOverrides=",":移除末尾多余的逗号
    -->
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <!-- 4. 判断 username 字段是否非空且非空字符串 -->
        <!-- 如果条件成立,添加 "username," 到字段列表 -->
        <if test="username != null and username != ''">
            username,
        </if>
        <!-- 5. 判断 sex 字段是否非空且非空字符串 -->
        <if test="sex != null and sex != ''">
            sex,
        </if>
        <!-- 6. 判断 age 字段是否非空(age 可能是 Integer/Long 类型) -->
        <if test="age != null">
            age,
        </if>
        <!-- 7. 判断 email 字段是否非空且非空字符串 -->
        <if test="email != null and email != ''">
            email,
        </if>
        <!-- 8. 判断 phone 字段是否非空且非空字符串 -->
        <if test="phone != null and phone != ''">
            phone,
        </if>
        <!-- 9. 判断 createTime 字段是否非空 -->
        <if test="createTime != null">
            create_time,  <!-- 注意:数据库字段名是蛇形命名 create_time -->
        </if>
    </trim>  <!-- 第一个 <trim> 结束,此时会生成类似 (username, sex, age) 的字段列表 -->
    
    <!-- 10. 第二个 <trim> 标签:处理 VALUES 部分 -->
    <trim prefix="VALUES (" suffix=")" suffixOverrides=",">
        
        <!-- 11. 对应 username 字段的值 -->
        <!-- 判断条件与字段列表的 username 判断保持一致 -->
        <if test="username != null and username != ''">
            #{username},  <!-- MyBatis 参数占位符 -->
        </if>
        <!-- 12. 对应 sex 字段的值 -->
        <if test="sex != null and sex != ''">
            #{sex},
        </if>  
        <!-- 13. 对应 age 字段的值 -->
        <if test="age != null">
            #{age},
        </if> 
        <!-- 14. 对应 email 字段的值 -->
        <if test="email != null and email != ''">
            #{email},
        </if>        
        <!-- 15. 对应 phone 字段的值 -->
        <if test="phone != null and phone != ''">
            #{phone},
        </if>
        <!-- 16. 对应 create_time 字段的值 -->
        <if test="createTime != null">
            #{createTime},  <!-- Java 对象属性名是驼峰命名 createTime -->
        </if>  
    </trim>  <!-- 第二个 <trim> 结束,此时会生成类似 (?, ?, ?) 的值列表 -->
    
</insert>  <!-- 整个插入语句结束 -->
相关推荐
默 语4 小时前
用Java撸一个AI聊天机器人:从零到一的踩坑实录
java·人工智能·spring·ai·机器人·spring ai
Logic1014 小时前
《数据库运维》 郭文明 实验2 MySQL数据库对象管理核心操作与思路解析
运维·数据库·mysql·学习笔记·计算机网络技术·形考作业·国家开放大学
脸大是真的好~4 小时前
黑马消息队列-rabbitMQ2-生产者重连机制-生产者确认机制-数据持久化-LazyQueue-消费者确认机制-失败重试机制-重试耗尽告警手动处理-
java·缓存·中间件
一 乐4 小时前
心理健康管理|基于springboot + vue心理健康管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
jiayong234 小时前
Spring XML解析与BeanDefinition注册详解
xml·java·spring
笃行客从不躺平4 小时前
WeakHashMap 学习
java
yuguo.im4 小时前
SQL 分析函数 `PERCENTILE_CONT` 的兼容性与深度解析
数据库·sql
Elastic 中国社区官方博客4 小时前
Elasticsearch:使用判断列表评估搜索查询相关性
大数据·数据库·elasticsearch·搜索引擎·单元测试·全文检索
Skrrapper4 小时前
【大模型开发之数据挖掘】2.数据挖掘的核心任务与常用方法
数据库·人工智能·数据挖掘