MySql实现无则插入有则更新的解决方案

摘要

某些场景会有这样的需求:无记录则插入,有记录则更新。例如:新增用户,以身份证号码作为唯一身份标识,插入时若先查询是否存在记录再决定插入还是更新,在高并发情况下必然存在问题。本文提供三种解决方案。

方案一 加锁

无论通过synchronized锁、ReentranLock锁还是分布式锁,都可以解决该问题。缺点是,加锁会影响性能。方法二和三都是数据库层面解决方案,个人感觉比方法一好一些。

方案二 Unique和Replace Into … SELECT …

首先对唯一性的字段添加唯一索引ALTER TABLE tb_name ADD UNIQUE (col1、col2...),,通过唯一索引即可保证数据的唯一性。

加入唯一索引后,通过INSERT INTO插入相同数据就会报错,此时需要使用REPLACE INTO插入数据,用法是一样的。通过REPLACE INTO插入数据时,若存在相同数据,会将之前的记录删除,再重新插入数据。缺点是,存在先删除再插入的过程,sql需要考虑全部数据列,不然会丢失部分列的数据。缺点是,建立唯一索引会影响插入效率。下面是具体的例子。

# 建立索引
ALTER TABLE user ADD UNIQUE (id_card);
# 假设user表只有id,name,id_card三个字段,且id字段自增。
# 现在需要插入name=ly,id_card=142733的记录。
# 但是,若之前存在id_card=142733的记录,修改name=ly即可。
REPLACE INTO user (id,name,id_card) 
SELECT id,'ly',142733 FROM user RIGHT JOIN (SELECT 1) AS tab 
ON user.id_card = 142733;

通过RIGHT JOIN (SELECT 1),若存在id_card=142733的记录,执行sql后会将原始id保存在临时的结果集中,随name和id_card一同插入。若不存在该记录,则将null作为id随name和id_card一同插入。最终实现

方案三 通过预插入语句判断是否存在记录

通过预插入语句,尝试插入,判断修改的记录是否大于0,若大于0表示插入成功,若为0则表示记录已存在,需要执行更新操作。

# 预插入
INSERT INTO user (name,id_card)
SELECT 'ly',142733 FROM DUAL 
WHERE NOT EXISTE (SELECT id_card FROM user WHERE id_card = 142733) ;
# 若预插入语句插入成功(修改记录数=1),则无需后续操作。否则执行更新操作。
UPDATE user SET name = 'ly' WHERE id_card = 142733;

通过NOT EXISTE条件,若存在id_card=142733的记录则伪表DUAL记录为空,预插入语句修改记录为0,此时需要执行更新操作。若不存在id_card=142733的记录,则伪表DUAL记录为一行且内容是'ly',142733,预插入语句修改记录为1,此时不必执行更新语句。

### MyBatis 中实现 Upsert 功能 在 MyBatis 或者 MyBatis Plus 中实现根据条件存在则更新记录,不存在则插入新记录(Upsert)功能可以通过多种方式完成。对于 MySQL 数据库而言,推荐使用 `INSERT ... ON DUPLICATE KEY UPDATE` 语法来处理这种情况。 #### 使用 XML 映射文件配置 SQL 语句 通过编写自定义的 SQL 插入语句,在遇到唯一键冲突时执行更新操作: ```xml <insert id="upsertRecord" parameterType="map"> INSERT INTO table_name (column1, column2, ...) VALUES (#{value.column1}, #{value.column2}, ...) ON DUPLICATE KEY UPDATE column1=VALUES(column1), column2=VALUES(column2); </insert> ``` 上述代码片段展示了如何利用 `<insert>` 标签配合 `ON DUPLICATE KEY UPDATE` 来构建一个能够自动判断是否存在重复项并相应采取行动的操作[^2]。 #### 利用 MyBatis Plus 的内置支持 MyBatis Plus 提供了一些便捷的方法用于简化 CRUD 操作,但对于复杂的业务逻辑如 Upsert,则可能仍需手动指定 SQL 语句。不过,可以借助其提供的接口和工具类更方便地组装查询参数。 例如,可以在实体类对应的 Mapper 接口中声明如下方法: ```java public interface UserMapper extends BaseMapper<User> { @Insert("<script>" + "INSERT INTO user (id,name,email)" + "VALUES (#{item.id},#{item.name},#{item.email})"+ "ON DUPLICATE KEY UPDATE name=#{item.name},email=#{item.email}" + "</script>") int insertOrUpdate(@Param("item")User item); } ``` 这段 Java 代码展示了一个名为 `insertOrUpdate` 的方法签名及其所关联的具体 SQL 实现细节[^1]。 #### 注意事项 - 当设计表结构时应确保设置了合适的唯一索引来触发 `ON DUPLICATE KEY UPDATE` 行为。 - 对于不同的数据库产品,具体的 Upsert 语法可能会有所不同;这里仅针对 MySQL 进行说明。 - 如果项目中已经集成了 MyBatis Plus 并希望保持一致性的话,尽可能采用框架本身提供的解决方案或扩展机制而非完全绕过它来自定义 SQL[^3]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值