使用 MyBatis `
INSERT INTO t_ocl_supcity (city_id, city_code, city_name, area_desc, sup_id, stat)
VALUES (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)
Oracle 報錯:ORA-00933: SQL 命令未正確結束。
Oracle 不支援標準 SQL 的 `VALUES (...), (...), ...` 多行插入語法。 此語法屬於 MySQL、PostgreSQL、SQL Server (2008+) 支援的擴展,Oracle 需用其他方式實現批量插入。
<insert id="batchInsert" parameterType="java.util.List">
INSERT ALL
<foreach collection="list" item="item" index="index" separator="">
INTO t_ocl_supcity (city_id, city_code, city_name, area_desc, sup_id, stat)
VALUES (
SEQ_OCL_SUPCITY.NEXTVAL,
#{item.cityCode,jdbcType=VARCHAR},
#{item.cityName,jdbcType=VARCHAR},
#{item.areaDesc,jdbcType=VARCHAR},
#{item.supId,jdbcType=VARCHAR},
#{item.stat,jdbcType=VARCHAR}
)
</foreach>
SELECT * FROM DUAL
</insert>
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO t_ocl_supcity (city_id, city_code, city_name, area_desc, sup_id, stat)
SELECT SEQ_OCL_SUPCITY.NEXTVAL, t.* FROM (
<foreach collection="list" item="item" separator="UNION ALL">
SELECT
#{item.cityCode,jdbcType=VARCHAR} city_code,
#{item.cityName,jdbcType=VARCHAR} city_name,
#{item.areaDesc,jdbcType=VARCHAR} area_desc,
#{item.supId,jdbcType=VARCHAR} sup_id,
#{item.stat,jdbcType=VARCHAR} stat
FROM DUAL
</foreach>
) t
</insert>
本質同方案一,寫法偏好差異。
<insert id="batchInsertPlsql" parameterType="java.util.List">
DECLARE
TYPE city_code_arr IS TABLE OF VARCHAR2(50);
TYPE city_name_arr IS TABLE OF VARCHAR2(100);
-- 其它欄位陣列...
v_codes city_code_arr := city_code_arr();
v_names city_name_arr := city_name_arr();
-- ...
BEGIN
<foreach collection="list" item="item" index="index">
v_codes.EXTEND; v_codes(#{index+1}) := #{item.cityCode,jdbcType=VARCHAR};
v_names.EXTEND; v_names(#{index+1}) := #{item.cityName,jdbcType=VARCHAR};
-- ...
</foreach>
FORALL i IN 1..v_codes.COUNT
INSERT INTO t_ocl_supcity (city_id, city_code, city_name, area_desc, sup_id, stat)
VALUES (SEQ_OCL_SUPCITY.NEXTVAL, v_codes(i), v_names(i), v_areas(i), v_sups(i), v_stats(i));
END;
</insert>
Oracle 序列 `NEXTVAL` 在單條 SQL 中每行調用一次。方案一、二中放在 `VALUES` 或 `SELECT` 列表即可。不要在 `
// Mapper 介面
int batchInsert(@Param("list") List<SupCity> list);
// SqlSession 執行(批量模式)
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
SupCityMapper mapper = session.getMapper(SupCityMapper.class);
for (List<SupCity> batch : Lists.partition(list, 1000)) { // 分批 1000
mapper.batchInsert(batch);
}
session.commit(); // 一次性提交
}
ExecutorType.BATCH:JDBC 批量處理,發送 `addBatch()` / `executeBatch()`
<!-- SupCityMapper.xml -->
<insert id="batchInsert" parameterType="java.util.List">
INSERT ALL
<foreach collection="list" item="item" separator="">
INTO t_ocl_supcity (city_id, city_code, city_name, area_desc, sup_id, stat)
VALUES (
SEQ_OCL_SUPCITY.NEXTVAL,
#{item.cityCode,jdbcType=VARCHAR},
#{item.cityName,jdbcType=VARCHAR},
#{item.areaDesc,jdbcType=VARCHAR},
#{item.supId,jdbcType=VARCHAR},
#{item.stat,jdbcType=VARCHAR}
)
</foreach>
SELECT * FROM DUAL
</insert>
// Service 實現
@Transactional
public void importCities(List<SupCityDTO> dtos) {
List<SupCity> entities = dtos.stream().map(this::toEntity).collect(toList());
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
SupCityMapper mapper = session.getMapper(SupCityMapper.class);
// 分批避免單條 SQL 過大
for (List<SupCity> batch : Lists.partition(entities, 500)) {
mapper.batchInsert(batch);
}
session.commit();
}
}