Mybatis如何实现InsertOrUpdate功能

目录
  • 实现InsertOrUpdate功能
    • 需求
    • 实现
  • Mybatis学习笔记:InsertOrUpdate
    • 环境
    • 前言
    • Dao
    • Mapper XML文件
    • 行别名
    • 主键和唯一索引

实现InsertOrUpdate功能

需求

最近在项目开发中遇到这样一个需求:每天需要对相同的数据(也有可能是不同的)进行两次入库操作,数据不存在便insert,存在则update。于是就用到了Mybatis的InsertOrUpdate功能。

实现

每次操作数据库之前,先根据id查询有没有记录,有则进行update操作,没有则进行insert操作。

model类代码如下。其中count为非业务字段(也不是表sheet中的字段),只是方便Mybatis进行insertOrUpdate操作的附加字段。

import lombok.Data;
@Data
public class Sheet {

    /**
     * 主键
     */
    private String id;
    /**
     * 客户姓名
     */
    private String customerName;
    /**
     * 。。。省略其他字段
     */

    /**
     * 该字段为非业务字段。Mybatis配置文件需要要到该字段,方便进行insertOrUpdate操作
     */
    private int count;
}

Mybatis的mapper.xml配置文件代码如下。

代码含义:先执行selectKey语句,把结果赋值给Sheet类的count属性。

  • 如果count大于0,表示记录已存在,则进行update操作。
  • 如果count等于0,表示没有记录,则进行insert操作。
<update id="insertOrUpdate" parameterType="Sheet" >
        <selectKey keyProperty="count" resultType="int" order="BEFORE">
            select count(1) from sheet where ID= #{id}
        </selectKey>
        <if test="count > 0">
            update sheet
            <set>
                <if test="customerName != null and customerName != ''">
                    CUSTOMER_NAME= #{customerName},
                </if>
            </set>
            where ID = #{id}
        </if>
        <if test="count==0">
            insert into sheet
            <trim prefix="(" suffix=")" suffixOverrides=",">
                ID,
                <if test="customerName != null and customerName != ''">
                    CUSTOMER_NAME,
                </if>
            </trim>
            <trim prefix="values (" suffix=")" suffixOverrides=",">
                #{id},
                <if test="customerName != null and customerName != ''">
                    #{customerName},
                </if>
            </trim>
        </if>
    </update>

selectKey标签可以给update标签中的parameterType属性(model类)对应的对象设置属性值。selectKey标签的属性描述:

  • keyProperty:selectKey 语句结果应该被设置的目标属性。此处对应的就是Sheet类的count属性。
  • resultType:结果的类型,此处为属性count的类型。
  • order:可以被设置为 BEFORE 或 AFTER。BEFORE表示先执行selectKey语句,后执行update语句;AFTER表示先执行update语句,后执行selectKey语句。

Mybatis学习笔记:InsertOrUpdate

环境

  • Intellij IDEA : 2021.3
  • Mysql:8+
  • java:1.8+

前言

以前使用mongodb、JOOQ组件的时候都是有insertOrUpdate的功能,现在使用mybatis似乎没有提供这种功能。

最近研究了,这个功能其实是mysql提供的,利用的是duplicate key update;

假设,我们有这么一张表:

CREATE TABLE `relation` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(64) NOT NULL DEFAULT '' COMMENT '名称',
  `relation_id` varchar(64) NOT NULL DEFAULT '' COMMENT '关联id',
  `type` int(11) NOT NULL DEFAULT '0' COMMENT '0:默认',
  `is_delete` tinyint(4) NOT NULL DEFAULT '0' COMMENT ' 状态值',
  `create_at` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人',
  `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_at` varchar(64) NOT NULL DEFAULT '' COMMENT '更新人',
  `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新人',
  PRIMARY KEY (`id`),
  UNIQUE KEY `ix_relation_id_type` (`relation_id`,`type`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

注意: ix_relation_id_type:唯一索引

Dao

@Mapper
public interface FlowModelMapper {
    void insertOrUpdate(List<FlowModel> flowModel);
}

Mapper XML文件

<insert id="insertOrUpdate">
    insert into flow_model(name, relation_id, type, is_delete,create_at,update_at)
    values
    <foreach collection="list" item="p" index="index" separator=",">
        (
        #{p.name},
        #{p.relationId},
        #{p.type},
        #{p.isDelete},
        #{p.createAt},
        #{p.updateAt}
        )
    </foreach>
    on duplicate key update
    name=values(name),
    update_at=values(update_at)
</insert>

说明:

  • on duplicate key update这个是非常关键的地方,需要有唯一键和主键。
  • on duplicate key update后面跟着的name=values(name)算是一个固定写法,作用:动态的传入要修改的值。

在MySQL 8.0.20之后,VALUES()在mysql未来的版本会被删除。

官方建议,使用列别名的方式来写:

<insert id="insertOrUpdate">
    insert into flow_model(name, relation_id, type, is_delete,create_at,update_at)
    values
    <foreach collection="list" item="p" index="index" separator=",">
        (
        #{p.name},
        #{p.relationId},
        #{p.type},
        #{p.isDelete},
        #{p.createAt},
        #{p.updateAt}
        )
    </foreach>
    AS fm
    on duplicate key update
    name=fm.name,
    update_at=fm.update_at
</insert>

行别名

insert into …values

语法:insert into ...values(...) AS 行别名 ON DUPLICATE KEY UPDATE 使用行别名。

例如:下面的 new就是行别名。

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new
  ON DUPLICATE KEY UPDATE c = new.a+new.b;

列别名

或者是:insert into ...values(...) AS 行别名(列别名,列别名,列别名) ON DUPLICATE KEY UPDATE 使用别名

下面的m,n,p是随便取的列别名

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new(m,n,p)
  ON DUPLICATE KEY UPDATE c = m+n;

注意:

当使用列别名时,必须在VALUES子句后面使用行别名,即使在后面的子句中不使用行别名。

除了insert into … values 场景,insert into …set场景也适用。

语法和上面是一样的:

INSERT INTO t1 SET a=1,b=2,c=3 AS new
  ON DUPLICATE KEY UPDATE c = new.a+new.b;
INSERT INTO t1 SET a=1,b=2,c=3 AS new(m,n,p)
  ON DUPLICATE KEY UPDATE c = m+n;

主键和唯一索引

现在假设我们有这些索引:

唯一索引:biz_id、name、code

主键:id

insert into template_url(id,name, code, url, scope, description,
    biz_id, create_by, create_user_id, update_by, update_user_id)
    values
      (
	1,'yutao','yutao','www.baidu.com','yutao','yutao',0,'yutao',0,'yutao',0
      )
    ON DUPLICATE KEY UPDATE
	name=values(name),
    description=values(description),
    url=values(url),
    scope=values(scope),
    update_by=values(update_by),
    update_user_id=values(update_user_id)

主键冲突

假设这时,主键冲突,那么MySQL就会接着判断是否 唯一索引冲突:

① 唯一索引不冲突,那么久执行更新

② 唯一索引冲突,就会报错:

1062 - Duplicate entry '0-yutao-yutao111' for key 'template_url.uk_biz_id_code_name', Time: 0.004000s

编辑时,唯一索引的字段不要修改

小结一下:insertOrUpdate的实现是基于mysql的on duplicate key update 来实现的。

使用ON DUPLICATE KEY UPDATE,如果行作为新行插入,则每行受影响的行值为1。如果更新现有行,则每行受影响的行值为2;如果将现有行设置为其当前值,则每行受影响的行值为0(可以通过配置,使其受影响的行值为1)。

官方地址:

13.2.6.2 INSERT … ON DUPLICATE KEY UPDATE Statement

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Mybatis Plus使用条件构造器增删改查功能的实现方法

    java后端层级结构 Controller 接口层 接口层比较好理解,它是面向web网络的接口,使用http格式去调用 /** * 图文课程管理Controller */ @RestController @RequestMapping("/driver/imageCourse") public class TImageCourseController extends BaseController { @Autowired private ITImageCourseService tIm

  • Mybatis 中的insertOrUpdate操作

    下面一段代码给大家介绍了Mybatis 中的insertOrUpdate操作,具体代码如下所示: <insert id="insertOrUpdate"> insert into base_person (pname, idcard, gender, nation, source_flag, create_time) values <foreach collection="list" item="p" index="i

  • 实例详解MyBatis-plus自动填充功能

    目录 什么是自动填充 1.这个功能是做什么的呢? 2.下面用一个例子来学习这个功能 3.测试 4.结果 什么是自动填充 有些表中会有更新时间.创建时间.更新人或者创建人这些字段. 每次对数据进行新增.删除.修改时都需要对这些字段进行设置.传统的做法是在进行这些操作前,对Entity的字段进行set设置,然后再进行操作.这种做法不仅容易忘记导致出错.而且代码会显得特别冗余. 虽然新增时间和修改时间可以使用数据库的时间,但是新增人和修改人就不能使用这样的功能. 所以MP就提供自动填充的功能,帮助自定

  • Mybatis中updateBatch实现批量更新

    目录 一.更新多条数据,每条数据都不一样 1.逐条更新(java实现) 2.逐条更新(mybatis实现) 3.sql批量更新(主力实现) 4.批量更新(单个字段,传参list),实际是sql批量更新的简化版本而已 5.sql批量更新(通过insert实现) 二.更新多条数据,更新的内容一样. 1.传map/传String 2.传map/传list 一.更新多条数据,每条数据都不一样 背景描述:通常如果需要一次更新多条数据有两个方式,(1)在业务代码中循环遍历逐条更新.(2)一次性更新所有数据(

  • Mybatis如何实现InsertOrUpdate功能

    目录 实现InsertOrUpdate功能 需求 实现 Mybatis学习笔记:InsertOrUpdate 环境 前言 Dao Mapper XML文件 行别名 主键和唯一索引 实现InsertOrUpdate功能 需求 最近在项目开发中遇到这样一个需求:每天需要对相同的数据(也有可能是不同的)进行两次入库操作,数据不存在便insert,存在则update.于是就用到了Mybatis的InsertOrUpdate功能. 实现 每次操作数据库之前,先根据id查询有没有记录,有则进行update操

  • jsp、struts、spring、mybatis实现前端页面功能模块化拆分的方案

    前端页面功能模块化拆分 当一个系统的功能很多时,不可能所有功能模块的页面都写在一个页面里面,这时就需要将不同功能模块的页面拆分出去,就像模板一样,需要哪块的功能就调用哪块的页面,然后加载相应数据并展示到相应的页面. 本应用的使用spring+struts+mybatis+jsp的方式,用两种方案来完成前端页面功能的拆分. 方案一: 在JSP页面中,利用EL表达式或者Java代码的方式,在后台完成页面数据的填充.然后在js中来完成页面的切换. jsp代码: 业务详情模块页面:riskDetailI

  • Spring MVC+mybatis实现注册登录功能

    本文实例为大家分享了Spring MVC mybatis实现注册登录功能的具体代码,供大家参考,具体内容如下 前期准备: 如下图所示,准备好所需要的包 新建工程,导入所需要的包,在web.xml中配置好所需要的,如下 <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee&q

  • mybatis一对多查询功能

    首先,我们还是先给出一个需求:根据订单id查询订单明细--我们知道,一个订单里面可以有多个订单的明细(需求不明确的同学,请留言或者去淘宝网上的订单处点一下就知道了).这个时候,一个订单,对应多个订单的id.这种需求出现的时候,我们应该如何查询呢? 此时我们的数据模型如下图(左)由于查询用户也是我们的需求,所以就在原有的基础上进行扩展,数据模型如下(右): 很显然,如果用resultType的方式去实现的话,是不合理的了.因为我们需要创建一个既有订单又有订单明细的pojo然后呢,我们的mybati

  • SpringBoot+mybatis+thymeleaf实现登录功能示例

    1.项目文件目录一栏 2.开始工作 先按照上图建立好相应的controller,mapper等文件. 接着进行一个配置 首先是application.properties server.port=8080#启动端口 #加载Mybatis配置文件 mybatis.mapper-locations = classpath:mapper/*.xml #数据源必填项 spring.datasource.driver-class-name= com.mysql.cj.jdbc.Driver spring.

  • Mybatis Limit实现分页功能

    1. Limit实现分页 1.1 为什么需要分页 减少数据的处理量 1.2 使用Limit实现分页 select * from user limit startIndex,pageSize; # 注意是从startIndex+1开始查询 pageSize 个 select * from user limit 3; # [0,3] 1.3 使用mybatis实现分页(核心:SQL) 1.3.1 接口 UserMapper.java // limit实现分页 Map后面只能是 Integer 包装类

  • Spring MVC+MyBatis+MySQL实现分页功能实例

    前言 最近因为工作的原因,在使用SSM框架实现一个商品信息展示的功能,商品的数据较多,不免用到分页,查了一番MyBatis分页的做法,终于是实现了,在这里记录下来分享给大家,下面来一起看看详细的介绍: 方法如下:  首先写一个分页的工具类,定义当前页数,总页数,每页显示多少等属性. /** * 分页 工具类 */ public class Page implements Serializable { private static final long serialVersionUID = -22

  • springmvc+spring+mybatis实现用户登录功能(上)

    由于本人愚钝,整合ssm框架真是费劲了全身的力气,所以打算写下这篇文章,一来是对整个过程进行一个回顾,二来是方便有像我一样的笨鸟看过这篇文章后对其有所帮助,如果本文中有不对的地方,也请大神们指教. 一.代码结构 整个项目的代码结构如图所示: controller为控制层,主要用于对业务模块的流程控制. dao为数据接入层,主要用于与数据库进行连接,访问数据库进行操作,这里定义了各种操作数据库的接口. mapper中存放mybatis的数据库映射配置.可以通过查看mybatis相关教程了解 mod

  • springmvc+spring+mybatis实现用户登录功能(下)

    昨天介绍了mybatis与spring的整合,今天我们完成剩下的springmvc的整合工作. 要整合springmvc首先得在web.xml中配置springmvc的前端控制器DispatcherServlet,它是springmvc的核心,为springmvc提供集中访问点,springmvc对页面的分派与调度功能主要靠它完成. 在我们之前配置的web.xml中加入以下springmvc的配置: web.xml <!-- Spring MVC 核心控制器 DispatcherServlet

随机推荐