Mybatis如何实现InsertOrUpdate功能
作者:Eaven25 发布时间:2022-10-11 10:26:58
实现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
来源:https://blog.csdn.net/qq_36065688/article/details/105328511


猜你喜欢
- 获取Spring中的bean有很多种方式,再次总结一下:第一种:在初始化时保存ApplicationContext对象Application
- controller传boolean形式值@GetMapping("/check-cart")public List&l
- 众所周知Java中的数据类型是强数据类型,基本数据类型之间的转换尤其固定的规则,当数据宽度比较窄的数据类型(如int)转换成数据类型比较宽的
- 今天在开发的过程中,遇到java.lang.ExceptionInInitializerError异常,百度查了一下,顺便学习学习,做个笔记
- spring in action第三版读书笔记spring3.0引入了spring expression language(spel)语言,
- 软硬件环境Windows 10Android studio 2.3.2OTT BOx with android 5.1.1前言App开发测试
- C++类返回值是*this成员函数当C++类的成员函数其返回值是*this时,表示返回值是调用该成员函数的变量的引用。例如:class A{
- Groovy 简介Groovy 是构建在 JVM 上的一个轻量级却强大的动态语言,它结合了 Python、Ruby 和 Smalltalk
- 对于初学java的同学来说,第一件事不是写hello world,而是搭建好java开发环境,下载jdk,安装,配置环境变量。这些操作在xp
- mybatisplus支持多种主键生成策略,默认采用认 ID_WORKER 即雪花算法雪花算法snowflflake是Twitter开源的分
- Java爬取图片现在开始学习爬虫,对于爬虫的入门来说,图片相对来说是比较容易获取的,因为大部分图片都不是敏感数据,所以不会遇到什么反爬措施,
- 用过新版本android 360手机助手都人都对 360中只在桌面显示一个小小悬浮窗口羡慕不已吧? 其实实现这种功能,主要有两步: 1.判断
- 前言:在本文中,我们将详细讨论Java中的一个核心概念——线程的生命周期。我们将使用一个快速的图解,
- 因为支持csv,所以就一块写上了Workbook,Worksheet using Aspose.Cells(第三方)把Excel读取到属性对
- 本文实例讲述了Android编程实现WebView添加进度条的方法。分享给大家供大家参考,具体如下:标准的XML界面<?xml ver
- trim中prefix与suffix等标签用法1.prefix 前缀增加的内容2.suffix 后缀增加的内容3.prefixOverrid
- private static void ExecuteSqlTransaction(string connectionString)&nbs
- 本文实例讲述了C#中ExecuteNonQuery()返回值注意点。对于C#数据库程序设计有一定的借鉴价值。分享给大家供大家参考之用。具体分
- 本文实例为大家分享了java实现马踏棋盘的具体代码,供大家参考,具体内容如下马踏棋盘算法介绍8X8棋盘,马走日字,要求每个方格只进入一次,走
- 产品在测试过程中发现一个bug,就是测试人员不停的疯狂的点击某个按钮,触发了toast以后,toast内容会一直排着队的显示出来,不能很快的