文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Mybatis如何实现InsertOrUpdate功能

2024-04-02 19:55

关注

实现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;
    
 
    
    private int count;
}

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

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

<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标签的属性描述:

Mybatis学习笔记:InsertOrUpdate

环境

前言

以前使用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>

说明:

在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

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

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯