Java书签 #解锁MyBatis的4种批量插入方式及ID返回姿势
1. 今日书签
项目开发中,我们经常会用到单条插入和批量插入。但是实际情况可能是,项目初期由于种种原因,在业务各处直接使用单条插入SQL进行开发(未开启批处理),在后面的迭代中,系统性能问题渐渐凸显,然后再通过技术优化,大面积的对单条插入SQL、单条更新SQL进行批量插入、批量更新优化。这不可取,但确实存在。
那数据的批量 insert/update 有几种方式实现呢?
哪些批量操作能直接获取到数据入库后的自增ID呢?
它们的优点、缺点是分别是什么呢?
在优化的过程中可能会出现哪些异常呢?我们要注意什么呢?…
这里我们列举4种数据批量保存方式,并对不同方式进行性能测试和对比分析。
2. 解锁方案
2.1. 前置任务
2.1.1. 标签
foreach 标签: myBatis-3-mapper.dtd 中 foreach 元素的属性主要有 item,index,collection,open,separator,close 这6种。
<foreach collection="" close="" index="" item="" open="" separator="">
| 属性 | 含义 |
|---|---|
| collection | 表示需要进行批量操作的对象集合 |
| item | 表示集合中每一个元素进行迭代时的别名 |
| index | 用于表示在迭代过程中,每次迭代到的位置 |
| open | 表示该语句以什么开始 |
| separator | 表示在每次进行迭代之间以什么符号作为分隔符 |
| close | 表示以什么结束 |
trim 标签:
<trim prefix="" suffix="" suffixOverrides="" prefixOverrides=""></trim>
| 属性 | 含义 |
|---|---|
| prefix | 在trim标签内SQL语句加上前缀 |
| suffix | 在trim标签内SQL语句加上后缀 |
| suffixOverrides | 去除trim标签内SQL语句多余的后缀内容 |
| prefixOverrides | 去除trim标签内SQL语句多余的前缀内容 |
案例:
MaBatis 生成批量插入SQL语句中:insert into t_item (id, product_id, product_name) values (1, ‘999’ , ), (2, ‘葡萄糖’ , ), …
MaBatis 生成批量插入SQL语句后:insert into t_item (id, product_id, product_name) values (1, ‘999’), (2, ‘葡萄糖’)
2.1.2. 配置
需要添加 JDBC 配置 allowMultiQueries=true,开启批处理。比如修改 mysql jdbc 的连接参数为:
spring.datasource.druid.wei.url = jdbc:mysql://192.168.1.1:3306/wei?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&allowMultiQueries=true
2.2. 四种数据批量插入方式
方式1:循环所有对象,生成固定字段的 VALUES
<insert id="batchSaveItemLogDo1" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">INSERT INTO t_item_operate_log (product_id, node_type, node_scene, node_data, `operator`) VALUES<foreach collection="list" item="item" index="index" separator=",">(#{item.productId,jdbcType=BIGINT}, #{item.nodeType,jdbcType=INTEGER}, #{item.nodeScene,jdbcType=INTEGER},#{item.nodeData,jdbcType=VARCHAR}, #{item.operator,jdbcType=VARCHAR})</foreach>
</insert>
这种批量插入的SQL使用了 MyBatis 的 foreach 标签来实现批量插入功能。与下面的SQL相比,它使用了更加简洁的语法来构建批量插入的SQL语句。比较常用。
优点:
- 语法简洁:使用 foreach 标签可以使SQL语句更加简洁,直观地表示批量插入的值。
- 性能优化:通过将多个插入值合并为一条INSERT语句,将所有数据一次性插入,可以减少与数据库的交互次数,从而提高性能。
- 自动生成主键:通过设置
useGeneratedKeys="true"和keyProperty="id",可以在插入数据时自动生成主键,并且将自动生成的主键值回写到Java对象中(通过 list 对象可拿到全部数据入库后的自增ID)。
缺点:
- 数据量限制:批量插入的数据量可能受到数据库配置和性能限制。对于非常大的数据量,需要考虑进一步优化或使用其他导入数据的方式。
- SQL注入风险:虽然这种方式使用了 MyBatis 的参数绑定,但仍需谨慎对待输入值,以防止SQL注入攻击。
- 不支持条件判断:这种方式将所有数据一次性插入,不支持对某些字段进行条件判断是否插入。如果需要在插入时根据条件判断是否插入某些字段,可参考下面的批量插入方式。
注意事项:
- 此方案 insert 的字段是固定的,不能动态适配
- 此批量插入方案须要保证传给持久层 Dao 接口的参数 list 对象(需要批量插入的对象)中每个对象的属性字段个数与SQL中指定的 insert 字段个数一致,且属性名一致,顺序不能错
异常案例: 如果 Dao 接口的参数 list 对象(需要批量插入的对象)中每个对象的属性字段个数与SQL中指定的 insert 字段个数不一致,或者,表中指定不允许为 NULL 的字段,在入参对象中其值为 NULL,则会出现异常:
### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'node_scene' cannot be null
### The error may exist in file [D:\Ct_ iSpace\tan\wei-saas\saas-persistence\target\classes\com\meiwei\tan\saas\persistence\item\ItemOperateLogDao.xml]
### The error may involve com.meiwei.tan.saas.persistence.item.mapper.ItemOperateLogDao.batchSaveItemLogDo1-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO t_item_operate_log ( product_id, node_type, node_scene, node_data, `operator` ) VALUES ( ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ? )
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'node_scene' cannot be null
; Column 'node_scene' cannot be null; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'node_scene' cannot be null
此异常原因是,我去掉了对象中 nodeScene 属性的设值,但插入SQL中需要插入 node_scene 该字段,而且表中该字段为非NULL无默认值。
方式2:循环所有对象,根据每个对象的不同字段值选择性生成 VALUES
<insert id="batchSaveItemLogDo2" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">INSERT INTO t_item_operate_log (product_id, node_type, node_scene, node_data, `operator`) VALUES<foreach collection="list" item="item" separator=","><if test="item != null"><trim prefix="(" suffix=")" suffixOverrides=","><if test="item.productId != null">#{item.productId,jdbcType=BIGINT},</if><if test="item.nodeType != null">#{item.nodeType,jdbcType=INTEGER},</if><if test="item.nodeScene != null">#{item.nodeScene,jdbcType=INTEGER},</if><if test="item.nodeData != null">#{item.nodeData,jdbcType=VARCHAR},</if><if test="item.operator != null">#{item.operator,jdbcType=VARCHAR},</if></trim></if></foreach>
</insert>
这种批量插入方式与上面的SQL非常相似,同样也是使用了MyBatis的foreach标签来实现批量插入功能。主要区别在于对空值的处理和SQL语句的拼接方式。比较常用。
优点:
- 空值处理:这种方式对空值的处理更加细致,在插入时能够将空字段插入为空字符串,避免插入NULL值。
- 性能优化:通过直接将每个字段值拼接在一起,可以减少拼接SQL语句的时间和内存开销,可能略微提高性能。
- 代码可读性:通过使用
trim标签和if条件,能够更清晰地看到插入值的拼接逻辑,SQL的拼接方式更灵活,使得代码更易读懂和维护。 - 自动生成主键:通过设置
useGeneratedKeys="true"和keyProperty="id",可以在插入数据时自动生成主键,并且将自动生成的主键值回写到Java对象中(通过 list 对象可拿到全部数据入库后的自增ID)。
缺点:
- 长SQL语句:这种方式生成的SQL语句字段越多SQL越长,尤其当数据量较大时,多个插入值的拼接逻辑可能导致SQL语句较长。
- 数据量限制:批量插入的数据量可能受到数据库配置和性能限制。对于非常大的数据量,需要考虑进一步优化或使用其他导入数据的方式。
注意事项:
- 此方案 insert 的字段是固定的,不能动态适配
备注: 此批量插入方式在 方式1 上有所优化,可以有效规避方案1中 Dao 接口的参数 list 对象(需要批量插入的对象)中每个对象的属性字段个数与SQL中指定的 insert 字段个数不一致,或者,表中指定不允许为 NULL 的字段,但在入参对象中其值为 NULL 的场景。
方式3:循环所有对象,为每个对象生成一个 INSERT 语句,一次执行多条SQL
<insert id="batchSaveItemLogDo3" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id" keyColumn="id"><foreach collection="list" item="item" separator=";">INSERT INTO t_item_operate_log (product_id, node_type, node_scene, node_data, `operator`) VALUES (#{item.productId,jdbcType=BIGINT}, #{item.nodeType,jdbcType=INTEGER}, #{item.nodeScene,jdbcType=INTEGER},#{item.nodeData,jdbcType=VARCHAR}, #{item.operator,jdbcType=VARCHAR})</foreach>
</insert>
这种批量插入的SQL使用了 MyBatis 框架的 动态SQL特性 和 foreach标签,将多条数据一次性插入到数据库中。
优点:
- 性能优化:使用批量插入可以减少与数据库的交互次数,从而提高性能。相较于逐条插入,批量插入能够减少网络开销和数据库连接/关闭的开销。
- 代码简洁:通过使用MyBatis的foreach标签,可以在SQL中直接处理Java集合(java.util.List)并且将多条数据一次性插入到数据库中,使得代码更加简洁、易读。
- 自动生成主键:通过设置
useGeneratedKeys="true"和keyProperty="id",可以在插入数据时自动生成主键,并且将自动生成的主键值回写到Java对象中。 - 可维护性:由于批量插入的SQL代码较为简洁,维护起来更加容易。
缺点:
- 一次性插入的数据量限制:虽然批量插入可以减少与数据库的交互次数,但一次性插入的数据量可能受到数据库配置和性能限制。对于非常大的数据量,可能需要考虑进一步优化或使用其他导入数据的方式。
- 数据库兼容性:不是所有的数据库都对批量插入提供了良好的支持。不同的数据库可能对批量插入的语法有所不同,这需要根据实际情况进行适配。
- SQL注入风险:使用MyBatis的foreach标签时,要确保在生成SQL语句时,正确处理输入值,以防止SQL注入攻击。
注意事项:
- 此方案 insert 的字段是固定的,不能动态适配
- 此方案批量插入后,只能获取到第1个对象的自增ID,拿不到全部数据入库后的自增ID
- 此方案需要对 DruidDataSource 的 WallConfig 属性
setMultiStatementAllow设置为 true(见下)
备注: 此方案比较少见。基本思路是组装好所有需要批量插入的对象后一次执行多条SQL。但这里的看似一次提交,实际是一条一条提交,所以效率比较慢。
@Configuration
public class MyBatisConfiguration {@Configuration@MapperScan(basePackages = {"com.meiwei.tan.saas.persistence.*"}, sqlSessionFactoryRef = "sqlSessionFactory4Wei")protected static class MyBatisDataSourceConfiguration4Wei {@Bean@Primary@ConfigurationProperties("spring.datasource.druid.wei")public DataSource dataSource4Wei() {DruidDataSource druidDataSource = new DruidDataSource();List<Filter> filterList = new ArrayList<>();filterList.add(wallFilter());filterList.add(statFilter());druidDataSource.setProxyFilters(filterList);return druidDataSource;}public WallFilter wallFilter() {WallFilter wallFilter = new WallFilter();wallFilter.setConfig(wallConfig());return wallFilter;}public StatFilter statFilter() {return new StatFilter();}public WallConfig wallConfig() {WallConfig wallConfig = new WallConfig();wallConfig.setMultiStatementAllow(true);return wallConfig;}}// ......
}
方式4:循环所有对象,根据 Mapper 接口或 Dao 接口传入的字段参数,动态可选生成 VALUES
<insert id="batchSaveItemLogDo4Selective" parameterType="map" useGeneratedKeys="true" keyColumn="id" keyProperty="list.id">INSERT INTO t_item_operate_log (<foreach collection="selective" item="column" separator=",">${column.escapedColumnName}</foreach>)VALUES<foreach collection="list" item="item" separator=",">(<foreach collection="selective" item="column" separator=","><if test="'product_id'.toString() == column.value"><if test="item.productId != null">#{item.productId,jdbcType=BIGINT}</if></if><if test="'node_type'.toString() == column.value"><if test="item.nodeType != null">#{item.nodeType,jdbcType=INTEGER}</if></if><if test="'node_scene'.toString() == column.value">#{item.nodeScene,jdbcType=INTEGER}</if><if test="'node_data'.toString() == column.value"><if test="item.nodeData != null">#{item.nodeData,jdbcType=VARCHAR}</if><if test="item.nodeData == null">''</if></if><if test="'operator'.toString() == column.value"><if test="item.operator != null">#{item.operator,jdbcType=VARCHAR}</if><if test="item.operator == null">''</if></if></foreach>)</foreach>
</insert>
这种批量插入方式与之前给出的SQL有相似之处,也是使用了 MyBatis 的 foreach标签 来实现批量插入功能。主要区别在于对字段列的选择和对空值的处理。
区别:
- 对列的选择:在这种方式中,通过使用
<foreach>标签和selective参数,可以动态选择要插入的列。${column.escapedColumnName}会根据传入的 selective参数,动态生成要插入的列名。 - 对空值的处理:对于插入的每个字段,通过
<if>条件判断,对空值进行了特殊处理。如果字段值为空,则插入为空字符串’',避免插入NULL值。
优势:
- 列的选择:通过selective参数,可以动态选择要插入的列,使得插入操作更灵活和可配置。
- 对空值的处理:与之前方式相似,对空值的处理更为细致,将空字段插入为空字符串,避免插入NULL值。
- 性能优化:通过将多个插入值合并为一条INSERT语句,可以减少与数据库的交互次数,从而提高性能。
- 自动生成主键:通过设置
useGeneratedKeys="true"和keyColumn="id" keyProperty="list.id",可以在插入数据时自动生成主键,并且将自动生成的主键值回写到Java对象中(通过 list 对象可拿到全部数据入库后的自增ID)。
劣势:
- 数据量限制:批量插入的数据量可能受到数据库配置和性能限制。对于非常大的数据量,可能需要考虑进一步优化或使用其他导入数据的方式。
- SQL注入风险:虽然这种方式使用了MyBatis的参数绑定,但仍需谨慎对待输入值,以防止SQL注入攻击。
注意事项:
- 与其它方案不同的是,此方案中 foreach 的每个 if SQL注入中,不要习惯性加逗号(,)哦,加了会报错
- 此方案需要对插入对象进行字段枚举配置,支持 Dao 接口接收需要插入的可选入参字段,见下文
备注: 该批量插入方案可解决其它方案中不能动态按入参字段进行动态组装生成SQL语句的问题,也可有效规避方案1中 Dao 接口的参数 list 对象(需要批量插入的对象)中每个对象的属性字段个数与SQL中指定的 insert 字段个数不一致问题,或者,表中指定不允许为 NULL 的字段,但在入参对象中其值为 NULL 的场景。
import lombok.Builder;
import lombok.Data;import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;@Data
@Builder
public class ItemOperateLogDO implements Serializable {private Long id;private Long productId;/*** 节点操作类型(1-新增;2-更新;3-删除)*/private Integer nodeType;private Integer nodeScene;private String nodeData;private String operator;private Date operatorTime;private static final long serialVersionUID = 1L;/*** This enum was generated by MyBatis Generator.* This enum corresponds to the database table t_operate_detail** @mbg.generated Tue Mar 02 10:29:28 CST 2021*/public enum Column {id("id", "id", "BIGINT", false),productId("product_id", "productId", "BIGINT", false),nodeType("node_type", "nodeType", "INTEGER", false),nodeScene("node_scene", "nodeScene", "INTEGER", false),nodeData("node_data", "nodeData", "VARCHAR", false),operator("operator", "operator", "VARCHAR", false);private static final String BEGINNING_DELIMITER = "\"";private static final String ENDING_DELIMITER = "\"";private final String column;private final boolean isColumnNameDelimited;private final String javaProperty;private final String jdbcType;public String value() {return this.column;}public String getValue() {return this.column;}public String getJavaProperty() {return this.javaProperty;}public String getJdbcType() {return this.jdbcType;}Column(String column, String javaProperty, String jdbcType, boolean isColumnNameDelimited) {this.column = column;this.javaProperty = javaProperty;this.jdbcType = jdbcType;this.isColumnNameDelimited = isColumnNameDelimited;}public String desc() {return this.getEscapedColumnName() + " DESC";}public String asc() {return this.getEscapedColumnName() + " ASC";}public static ItemOperateLogDO.Column[] excludes(ItemOperateLogDO.Column... excludes) {ArrayList<ItemOperateLogDO.Column> columns = new ArrayList<>(Arrays.asList(ItemOperateLogDO.Column.values()));if (excludes != null && excludes.length > 0) {columns.removeAll(new ArrayList<>(Arrays.asList(excludes)));}return columns.toArray(new ItemOperateLogDO.Column[]{});}public static ItemOperateLogDO.Column[] all() {return ItemOperateLogDO.Column.values();}public String getEscapedColumnName() {if (this.isColumnNameDelimited) {return new StringBuilder().append(BEGINNING_DELIMITER).append(this.column).append(ENDING_DELIMITER).toString();} else {return this.column;}}public String getAliasedEscapedColumnName() {return this.getEscapedColumnName();}}
}
2.3. Dao接口(Mapper接口)
如上各个批量SQL对应的 Mapper 接口(Dao 接口):
public interface ItemOperateLogDao {int batchSaveItemLogDo0(ItemOperateLogDO record);int batchSaveItemLogDo1(@Param("list") List<ItemOperateLogDO> list);int batchSaveItemLogDo2(@Param("list") List<ItemOperateLogDO> list);int batchSaveItemLogDo3(@Param("list") List<ItemOperateLogDO> list);int batchSaveItemLogDo4Selective(@Param("list") List<ItemOperateLogDO> list, @Param("selective") ItemOperateLogDO.Column ... selective);
}
2.4. Service接口
public interface ItemOperateLogService {/** 循环所有对象,普通单条插入(未开启批处理) */Map<String, String> batchSaveItemLogDo0(List<ItemOperateLogDO> itemOperateLogDOList);/** 方案1:循环所有对象,生成固定字段的 VALUES */Map<String, String> batchSaveItemLogDo1(List<ItemOperateLogDO> itemOperateLogDOList);/** 方案2:循环所有对象,根据每个对象的不同字段值选择性生成 VALUES */Map<String, String> batchSaveItemLogDo2(List<ItemOperateLogDO> itemOperateLogDOList);/** 方案3:循环所有对象,为每个对象生成一个 INSERT 语句,一次执行多条SQL */Map<String, String> batchSaveItemLogDo3(List<ItemOperateLogDO> itemOperateLogDOList);/** 方案4:循环所有对象,根据 Mapper 接口或 Dao 接口传入的字段参数,动态可选生成 VALUES */Map<String, String> batchSaveItemLogDo4Selective(List<ItemOperateLogDO> itemOperateLogDOList);
}
2.5. 测试结果
批量保存方法(batchSaveItemLogDo0)插入数据1000条,耗时15666毫秒,批量保存后获取自增ID抽样结果:{"第1个对象的自增ID":"1","中间对象的自增ID":"500","最后1个对象的自增ID":"1000"}
批量保存方法(batchSaveItemLogDo1)插入数据1000条,耗时360毫秒,批量保存后获取自增ID抽样结果:{"第1个对象的自增ID":"1001","中间对象的自增ID":"1500","最后1个对象的自增ID":"2000"}
批量保存方法(batchSaveItemLogDo2)插入数据1000条,耗时490毫秒,批量保存后获取自增ID抽样结果:{"第1个对象的自增ID":"2001","中间对象的自增ID":"2500","最后1个对象的自增ID":"3000"}
批量保存方法(batchSaveItemLogDo3)插入数据1000条,耗时10583毫秒,批量保存后获取自增ID抽样结果:{"第1个对象的自增ID":"3001","中间对象的自增ID":"null","最后1个对象的自增ID":"null"}
批量保存方法(batchSaveItemLogDo4)插入数据1000条,耗时365毫秒,批量保存后获取自增ID抽样结果:{"第1个对象的自增ID":"4001","中间对象的自增ID":"4500","最后1个对象的自增ID":"5000"}
2.6. 性能对比
1)横向性能对比分析

通过4种批量插入方案横向对比可以发现:
- 方案3效率最低,即一条数据一条SQL的批量插入方式是一种伪批处理,它有多少条数据有会有多少条插入SQL,就需要执行提交多少次,所以效率相对较低。
- 方案4会稍慢点,对于少量数据的批量插入操作,且想动态组装插入字段时可以考虑这个方案来实现。但是,可以看到,随着数据量的增多,其性能会急剧下降。
- 方案2与方案1各有特点:方案1适合无差别的全字段批量插入,方案2适合字段判空批量插入。
所以,使用时需要根据实际应用场景进行方案选择,以及注意其方案的优劣和注意事项。
2)纵向性能对比分析

通过两种数据量切片批量插入效果来看:通过调整切片列表大小,在一定范围内,可以有效提高批量插入效率。如上案例,从200一批,调到500一批,数据量超过10000时,500一批的批量插入要比200一批的速度快。
但要注意的是,在 MySQL 5.7 及更早版本中,数据包大小默认设置是 4M。如果不注意实际场景,一味调大切片大小,很可能会造成 SQL 语句执行失败。
异常如:“Could not execute JDBC batch update”。
虽然可以通过修改 mysql 的配置文件(my.cnf 或 my.ini)中的 max_allowed_packet = 8M 来解决,但是大的SQL同样可能会异变成慢SQL,得不偿失。
注意: 修改 max_allowed_packet 的值时,应该根据实际需求和数据库负载进行合理的调整,避免设置过大导致资源浪费或设置过小无法满足插入或更新大数据量的需求。比如:批量插入比较简单的字段和信息时,切片大小可以调到500;但如果,批量记录的是复杂的信息,一个字段可能就是 3000 Varchar 时,那500大小的切片显然不合适。所以,实际中需要先评估数据量与字段多少,再看合适与否。
3. 共性异常
附带几种批量SQL中常见的异常:
3.1. 如果 SQL 中 foreach 标签下,collection="list" 配置的 list 与 Dao 接口入参对象使用的 @Param("list") 名称不一致,如 collection="productList",则会出现异常:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'lists' not found. Available parameters are [list, param1]
3.2. 如果 SQL 中 foreach 标签下,SQL注入时未使用 item. 规则循环每个对象,不管是一个属性未使用 item.,还是多个属性未使用 item.(此处 foreach 中的 item="item" 只是列举,item 也可以自定义名称),则会出现异常:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'nodeType' not found. Available parameters are [list, param1]
3.3. 如果 SQL 中 foreach 标签下,编写的属性名称与接口入参对象中的属性名称不一致,则会出现异常:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'nodeTypes' in 'class com.meiwei.tan.saas.persistence.item.model.ItemOperateLogDO'
4. 其它批量插入操作
4.1) 如果需要批量插入数据,并且希望在遇到重复键值或主键冲突时避免报错,可以使用
INSERT IGNORE 或 INSERT … ON DUPLICATE KEY UPDATE
这两种方法在插入时能够处理重复键的情况,前者忽略重复键,后者在遇到重复键时更新记录。
语法如下:
使用 INSERT IGNORE:
INSERT IGNORE INTO your_table (column1, column2, column3)
VALUES (value1, value2, value3),(value4, value5, value6),...;
使用 INSERT … ON DUPLICATE KEY UPDATE:
INSERT INTO your_table (column1, column2, column3)
VALUES (value1, value2, value3),(value4, value5, value6),...
ON DUPLICATE KEY UPDATE column1 = VALUES(column1), column2 = VALUES(column2), ...;
- INSERT IGNORE: 如果插入数据时发生了主键或唯一索引冲突,即要插入的记录已存在,那么数据库会忽略该条插入操作,而不会报错。如果插入的数据与已有记录冲突,该插入操作会被静默丢弃,不会触发错误,也不会返回自动生成的主键。
- INSERT … ON DUPLICATE KEY UPDATE: 如果插入数据时发生了主键或唯一索引冲突,即要插入的记录已存在,那么数据库会执行更新操作,而不是插入新记录。同时,该语句也可以用于返回自动生成的主键。
4.2) 如果需要导入大量数据的情况,比如从CSV文件导入数据
LOAD DATA INFILE 语句可以通过读取本地文件将大量数据一次性导入数据库表中,这样可以实现高效的批量插入(仅适用于MySQL等数据库)。
注意: 在使用 LOAD DATA INFILE 语句时,数据直接从文件导入到数据库表中,绕过了MySQL的INSERT语句,因此不会触发INSERT操作,也就不会返回自动生成的主键。
语法如下:
LOAD DATA INFILE 'file_path'
INTO TABLE your_table
FIELDS TERMINATED BY ',' -- 或其他分隔符
LINES TERMINATED BY '\n' -- 或其他行终止符
(column1, column2, column3);
5.总结
无论使用哪种方法,都应根据具体的需求和数据量选择最适合的方式。对于大量数据的批量插入,使用 LOAD DATA INFILE 通常会比INSERT 语句更高效。而对于少量数据的批量插入,使用 INSERT 语句可能更简便。此外,如果遇到可能会导致主键冲突的情况,可以选择合适的方法来处理重复键值。
相关文章:
Java书签 #解锁MyBatis的4种批量插入方式及ID返回姿势
1. 今日书签 项目开发中,我们经常会用到单条插入和批量插入。但是实际情况可能是,项目初期由于种种原因,在业务各处直接使用单条插入SQL进行开发(未开启批处理),在后面的迭代中,系统性能问题渐…...
在react项目中如何引入国际化
react-i18next 在 React 项目中引入国际化(Internationalization,简称 i18n)可以使用第三方库来实现。其中,最常用且流行的国际化库是 react-i18next,它基于 i18next 实现,提供了方便易用的国际化功能。下…...
spring学习笔记十三
注解实现管理第三方Bean和为第三方Bean注入资源 1、添加pom坐标 <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency> 2、SpringConfig配置类 Configuratio…...
react native 本地存储 AsyncStorage
An asynchronous, unencrypted, persistent, key-value storage system for React Native. Async Storage 只能用来储存字符串数据,所以为了去储存object类型的数据,得先进行序列化(JSON.stringify())当你想要使用数据的时候&…...
Postgresql数据库中的时间类型汇总
PostgreSQL数据库有以下几种时间类型 1 日期 date:表示日期,格式为YYYY-MM-DD。 2 时间 time:表示时间,格式为HH:MI:SS。 3 日期和时间 timestamp:表示日期和时间,格式为YYYY-MM-DD HH:MI:SS。 4 带…...
算法刷题Day 51 最佳买卖股票时机含冷冻期+买卖股票的最佳时期含手续费
Day 51 动态规划 309. 最佳买卖股票时机含冷冻期 关键是要画出状态转移图 然后根据状态转移图来写状态转移方程 class Solution { public:int maxProfit(vector<int>& prices) {int len prices.size();vector<vector<int>> dp(len, vector<int&g…...
编程导航算法村 第五关 | 白银挑战
编程导航算法村 第五关 | 白银挑战 用栈实现队列 LeetCode 232题 class MyQueue {private Stack<Integer> stack; // 保存private Stack<Integer> tempstack; // 临沭队列public MyQueue() {stack new Stack<>();tempstack new Stack<>();}public…...
(十六十七)时序数据库是怎么存储用户名和密码的从InfluxDB OSS迁移数据
以下内容来自 尚硅谷,写这一系列的文章,主要是为了方便后续自己的查看,不用带着个PDF找来找去的,太麻烦! 第 16 章 时序数据库是怎么存储用户名和密码的 1、InfluxDB内部自带了一个用Go语言写的BlotDB,Blo…...
5分钟开发一个AI论文抓取和ChatGPT提炼应用
5分钟开发一个AI论文抓取和ChatGPT提炼应用 第一步 点击“即刻开始” -选择模板 python -修改标题 “AIPaper”,项目标识“AIPaper”,点击“创建项目” 第二步 在编程区域右侧AI区域,输入框输入以下内容: 请根据下面的内容&…...
SK5代理与网络安全:保障爬虫隐匿性与HTTP连接稳定性
一、SK5代理简介 SK5代理,即socks5代理,是一种网络协议,用于在客户端和服务器之间进行数据传输。相比其他代理协议,如HTTP代理,SK5代理具有更高的性能和安全性,支持TCP和UDP连接,并可以处理更复…...
基于4G网络的嵌入式设备远程升级系统设计与实现(学习一)
摘要 随着无线通信技术的不断更新发展,嵌入式设备的联网应用领域得以大规模扩大,远程升级功能成为产品开发中必不可少的一部分。 本文对嵌入式设备远程升级进行了研究,在不改变设备硬件集成度基础上,设计实现了分离式升级的远程…...
陪诊小程序软件|陪诊系统定制|医院陪诊小程序
开发一个陪诊小程序需要投入一定的费用,具体金额会因项目的复杂程度、功能需求和推广政策而有所差异在投入资金之前,建议进行市场调研和需求分析,制定出合理的预算,并选择专业的开发团队进行合作,那么开发陪诊小程序需…...
[数据集][目标检测]空中飞鸟目标检测数据集VOC格式4955张
数据集名称:空中飞鸟数据集VOC-4955张 数据集制作单位:未来自主研究中心(FIRC) 图片数量(jpg文件个数):4955 标注数量(xml文件个数):4955 标注类别数:1 标注类别名称:["bird"] 每个类别标注的框数࿱…...
安徽现货黄金代理请看这篇
持续两三年的新冠疫情,令全球经济遭受不同程度的打击,很多传统的行业更是重灾区,当中不少从业多年的朋友表示虽然看不清前进,但也不敢随便转行,如果那么有一份这样的工作,既不用他们离开本职,也…...
HTML JS实现点击按钮下载文件功能例子(C知道版)
其实这篇应该算是一篇“水”文章,为什么要这么“水”呢,除了最近南方的气候闷热难耐需要降温之外,另一个主要原因,这里面所写的代码均是由CSDN的AI文本大模型"C知道"完成,我在这里只是简单记录一下ÿ…...
企业网络安全与数据保护合规建设 ——从合规运营到香港上市
序言 《企业网络安全与数据保护合规建设 ——从合规运营到香港上市(一)》梳理了我国网络安全与数据保护领域近期主要立法情况,本文将着重分析拟赴港上市企业运营阶段的数据合规要点以期为拟赴港上市的相关企业提供有益的参考。 二 企业运营…...
antdv Select dropdownRender Input 不能输入的问题
简言之:外层套div,然后利用Select的open属性。直接上代码: <template><a-form-item-rest><div click"selOpen !selOpen"><Selectv-model:value"xxx"placeholder"请选择":options"g…...
PostgreSQL 查询json/jsonb是否存在某个片段
文章目录 前言实现实现思路坑1坑2坑3 恍然大悟 前言 在PostgreSQL中,jsonb有额外的操作符,如 >、<、?、?|、?& 可以用来查询是否包含路径/值,以及顶层键值是否存在。 详细文章:PostgreSQL 操作json/jsonb 那么&am…...
Spring 官方文档及相关资料的网址集合
文章目录 MavenSpringSpring FrameworkSpring BootSpring Cloud AlibabaNacos Maven Maven 仓库依赖包官方查询通道:https://mvnrepository.com/ Maven 插件官方文档:https://maven.apache.org/plugins/ 安卓依赖包官方查询通道*:https://m…...
hypery 十一、命令行
教程:Hyperf symfony/console composer地址: symfony/console - Packagist github地址:GitHub - symfony/console: Eases the creation of beautiful and testable command line interfaces hyperf/command github地址:https://github.com/…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
