如何使用Quarkus Reactive Hibernate Panache正确实现批量插入
问题根因
你代码报错+批量插入不生效是三个问题共同导致的:
- 直接报错原因:
Mutiny.Session.persistAll是可变参数方法,签名为Uni<Void> persistAll(Object... entities),你直接传入List类型参数时,JVM会把整个List对象当作单个待持久化实体传入,Hibernate识别不到ArrayList是注册的实体类,直接抛出Unknown entity: java.util.ArrayList异常。 - API使用错误:
session.setBatchSize(BATCH_SIZE)是无返回值的配置方法,你把它的返回值赋值给batch变量,后续调用batch.persistAll本身就是逻辑错误。 - 批量不生效核心原因:你的实体主键用了
IDENTITY生成策略,Hibernate(包含Reactive版本)对IDENTITY策略默认禁用JDBC批量插入——该策略要求每次插入单条记录后立刻从数据库获取自增主键,无法攒批提交。
正确实现方案
第一步:修改实体主键生成策略
把IDENTITY策略替换为SEQUENCE(支持MySQL 8.0+、PostgreSQL等绝大多数主流数据库),allocationSize配置为和你后续设置的批量大小一致,性能最优:
@Setter(NONE) @Id @GeneratedValue(strategy = SEQUENCE, generator = "kline_seq") @SequenceGenerator(name = "kline_seq", sequenceName = "kline_id_seq", allocationSize = 50) @Column(name = "id", nullable = false, updatable = false) private Long id;
第二步:修正持久化逻辑
不要做阻塞调用,直接链式操作session,传入数组类型的实体列表给persistAll,插入后flush并清空一级缓存避免内存溢出:
private static final int BATCH_SIZE = 50; @ReactiveTransactional public Uni<List<KlineEntity>> persistAll(List<KlineEntity> klines) { return Panache.getSession() .invoke(session -> session.setBatchSize(BATCH_SIZE)) .chain(session -> session.persistAll(klines.toArray()) .chain(session::flush) .invoke(session::clear) .replaceWith(klines) ); }
如果用Active Record/Panache Repository模式,写法可以更简洁:
@ReactiveTransactional public Uni<List<KlineEntity>> persistAll(List<KlineEntity> klines) { return Panache.getSession() .invoke(session -> session.setBatchSize(BATCH_SIZE)) .chain(session -> session.persistAll(klines.toArray())) .chain(Panache::flush) .chain(() -> Panache.getSession().invoke(Session::clear)) .replaceWith(klines); }
第三步:添加必要配置
在application.yml中添加对应配置(如果用properties格式自行转换结构即可):
quarkus: hibernate-orm: jdbc: # 全局配置JDBC批量大小,和代码里设置的batchSize保持一致 statement-batch-size: 50 datasource: reactive: # PostgreSQL加参数开启批量语句重写,性能提升明显 url: postgresql://数据库地址:5432/trading?reWriteBatchedInserts=true # 如果是MySQL,url替换为下面的参数 # url: mysql://数据库地址:3306/trading?rewriteBatchedStatements=true
注意事项
- 你实体中
pair关联字段配置了CascadeType.ALL,批量插入时如果传入的KlineEntity关联了未持久化的CurrencyPairEntity,Hibernate会自动级联插入,注意提前去重避免唯一键冲突。 - 批量大小建议设置在20-100区间,数值过大会导致单次提交的数据包体积过大,反而降低插入性能。
- 如果需要插入超大规模数据(10w条以上),建议分批次循环调用上述方法,单次提交不要超过1000条,避免内存占用过高。
内容的提问来源于stack exchange,提问作者Frédéric Thomas
相关产品推荐
相关产品推荐

