java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring JDBC批量操作方式

Spring Framework中JDBC批量操作的三种实现方式

作者:lang20150928

本文详细介绍了如何使用 Spring 的 JdbcTemplate 进行高效的数据库批量更新,从而减少与数据库之间的网络往返次数(round-trips),提升性能,下面我将用通俗易懂的方式,结合代码示例和实际场景给大家详细说说,需要的朋友可以参考下

这篇文章详细介绍了如何使用 Spring 的 JdbcTemplate 进行高效的数据库批量更新,从而减少与数据库之间的网络往返次数(round-trips),提升性能。

下面我将用通俗易懂的方式,结合代码示例和实际场景,帮你系统地理解这一节的核心思想和三种主要批量处理方式。

为什么要用“批量操作”?

在没有批量处理时,如果你要插入或更新 1000 条数据:

for (Actor actor : actors) {
    jdbcTemplate.update("INSERT INTO t_actor ...", actor.getName(), ...);
}

这会向数据库发送 1000 次独立的 SQL 请求 → 1000 次网络通信 → 效率极低。

而使用 批量操作(Batch Operations),你可以把多个 SQL 操作“打包”成一组,一次性提交给数据库执行:

✅ 结果:

基本批量操作:使用 BatchPreparedStatementSetter

这是最传统、控制最精细的方式。

使用方法:

实现 BatchPreparedStatementSetter 接口的两个方法:

  1. getBatchSize():返回本次批量操作的总条数
  2. setValues(PreparedStatement ps, int i):为第 i 条记录设置参数

示例说明:

public int[] batchUpdate(final List<Actor> actors) {
    return this.jdbcTemplate.batchUpdate(
            "update t_actor set first_name = ?, last_name = ? where id = ?",
            new BatchPreparedStatementSetter() {
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    Actor actor = actors.get(i);
                    ps.setString(1, actor.getFirstName());
                    ps.setString(2, actor.getLastName());
                    ps.setLong(3, actor.getId().longValue());
                }
                public int getBatchSize() {
                    return actors.size(); // 整个列表作为一批
                }
            });
}

关键点:

特殊情况:流式输入(不确定总数)

如果数据来自文件、流或数据库游标,无法预知总数,可以使用:

InterruptibleBatchPreparedStatementSetter

它有一个额外方法:

boolean isBatchExhausted();

当你读完所有数据后返回 true,Spring 就停止继续添加语句。

更简洁的方式:用对象列表做批量操作

Spring 提供了更简洁的 API —— 不用手动实现接口,只需传入一个对象列表!

方式一:使用命名参数(Named Parameters) + NamedParameterJdbcTemplate

public int[] batchUpdate(List<Actor> actors) {
    return this.namedParameterJdbcTemplate.batchUpdate(
        "update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
        SqlParameterSourceUtils.createBatch(actors) // 自动提取 getter
    );
}

优点:

要求:Actor 类必须有 getFirstName()、getId() 等标准 getter 方法

方式二:使用 ? 占位符 + List<Object[]>

List<Object[]> batch = new ArrayList<>();
for (Actor actor : actors) {
    Object[] values = new Object[] {
        actor.getFirstName(), 
        actor.getLastName(), 
        actor.getId()
    };
    batch.add(values);
}

jdbcTemplate.batchUpdate(
    "update t_actor set first_name = ?, last_name = ? where id = ?", 
    batch
);

优点:

注意事项:

性能提示:

Spring 默认调用 ParameterMetaData.getParameterType() 来判断 null 值的类型,但某些数据库驱动(如 Oracle 12c)这个操作很慢。
可以通过设置系统属性关闭:

# 在 JVM 启动参数或 spring.properties 中添加
spring.jdbc.getParameterType.ignore=true

或者显式指定类型(更推荐)。

处理超大数据集:分多个小批次提交(Multiple Batches)

前面的例子都是一次性提交全部数据。但如果数据量太大(比如 10 万条),一次性加载到内存会导致内存溢出(OOM)。

解决方案:

使用分批提交(chunking),每 100 条提交一次。

public int[][] batchUpdate(final Collection<Actor> actors) {
    int[][] updateCounts = jdbcTemplate.batchUpdate(
        "update t_actor set first_name = ?, last_name = ? where id = ?",
        actors,              // 所有数据
        100,                 // 每批最多 100 条
        (ps, actor) -> {     // Lambda 设置参数
            ps.setString(1, actor.getFirstName());
            ps.setString(2, actor.getLastName());
            ps.setLong(3, actor.getId().longValue());
        }
    );
    return updateCounts;
}

返回值解释:

int[][] updateCounts

例如:

[
  [1, 1, 1, ..., 1],  // 第1批,共100条,每条影响1行
  [1, 1, 1, ..., 1],  // 第2批
  [1, 1, 1]           // 最后一批只有3条
]

适用场景:

三种方式对比总结

方式适用场景优点缺点
BatchPreparedStatementSetter需要精确控制每条记录参数灵活、支持中断写法较繁琐
List<Object[]>SqlParameterSourceUtils.createBatch()数据已在内存中,结构简单写法简洁不适合超大数据集
分批提交(multiple batches)超大数据集内存友好、可控性强稍复杂

性能建议

合理设置批大小(batch size)

开启数据库的批量支持

jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true

使用连接池(如 HikariCP)

避免自动提交(Auto-commit)

实际应用建议(Spring Boot 项目)

如果你使用的是 Spring Boot,推荐写法如下:

@Service
public class ActorService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void updateActorsInBatches(List<Actor> actors) {
        jdbcTemplate.batchUpdate(
            "UPDATE t_actor SET first_name = ?, last_name = ? WHERE id = ?",
            actors,
            100,
            (ps, actor) -> {
                ps.setString(1, actor.getFirstName());
                ps.setString(2, actor.getLastName());
                ps.setLong(3, actor.getId());
            }
        );
    }
}

搭配事务注解:

@Transactional
public void importHugeData() {
    List<Actor> hugeList = readFromCsvOrDatabase();
    updateActorsInBatches(hugeList); // 分批提交
}

总结一句话:

批量操作 = 把多条 SQL “打包”发送给数据库,减少网络来回,提高性能。Spring 提供了多种方式让你既能简单使用,也能精细控制。

下一步你可以思考:

以上就是Spring Framework中JDBC批量操作的三种实现方式的详细内容,更多关于Spring JDBC批量操作方式的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文