我有一个父/子单向关系。当检查日志时,我看到每个子行都有一个单独的插入查询,相当于我们说:
insert into childTable(col1, col2) values(val1, val2);
insert into childTable(col1, col2) values(val3, val4);
在单个查询中插入所有行会更有效吗?类似于以下内容:
insert into childTable(col1, col2) values(val1, val2), (val3, val4)
有没有一种方法可以强制JPA生成多行插入而不是单行插入?
编辑:我当前正在使用级联插入,因此我插入了父级,而子级的插入是自动生成的。我宁愿继续使用该方法,而不是手动创建一个巨大的SQL查询,因为我认为级联插入会产生更干净的代码。
我已经定期刷新 session 以控制L1缓存的大小,因此用尽内存不是问题。
最佳答案
实际上,在单个查询中插入所有行的效率较低。
首先,有几点观察:
在幕后,Hibernate对其代表您执行的每个查询都使用
PreparedStatement
,并将其缓存和重用。 MySQL会缓存“编译” SQL语句。在不陷入细节的情况下,对基础技术进行了高度优化,可以多次运行相对少量的查询。如果将插入作为单个语句执行,则每次要插入的值数量不同时,都必须编译和缓存新的SQL(可能从缓存中推送另一个查询),这会增加开销。当您每次只使用相同的SQL时,可以避免这种开销。
由于许多原因,您必须在SQL中使用绑定(bind)变量,Hibernate会自动为您执行此操作。如果您执行一些自定义查询来测试一次插入式方法,则绝对也应该使用绑定(bind)变量。
另一个注意事项是如何生成标识符。如果它是通过数据库中的标识列,则Hibernate需要为每一列接收ID,这通常仅在创建一行时才可能。因此,出于效率考虑,最好使用基于序列的标识符生成器,并在客户端缓存序列值。
我刚刚注意到您的编辑:我的经验是Hibernate在处理插入父子数据时会“额外”更新。即使我只有多对一关系,我也通过将映射更改为具有“join”表(就像您看到的多对多关系)来设法获得“纯”插入。就我而言,向三个表中进行大量插入要快得多,而向两个表中进行插入和更新操作要少得多。如果您担心性能,则绝对应该计划一些时间来调整Hibernate配置。