介绍
JDBC长期以来一直为DML语句批处理提供支持。 默认情况下,所有语句都一个接一个地发送,每个语句都在单独的网络往返中发送。 批处理使我们能够一次性发送多个语句,从而节省了不必要的套接字流刷新。
Hibernate将数据库语句隐藏在事务后写抽象层的后面 。 中间层允许我们从持久层逻辑中隐藏JDBC批处理语义。 这样,我们可以更改JDBC批处理策略,而无需更改数据访问代码。
配置Hibernate来支持JDBC批处理并不是那么容易,所以我将解释为使其工作所需要做的一切。
测试时间
我们将从以下实体模型开始:
该帖子与Comment实体具有一对多关联:
@OneToMany(
cascade = CascadeType.ALL,
mappedBy = "post",
orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
或者测试场景同时发出INSERT和UPDATE语句,因此我们可以验证是否正在使用JDBC批处理:
LOGGER.info("Test batch insert");
long startNanos = System.nanoTime();
doInTransaction(session -> {
int batchSize = batchSize();
for(int i = 0; i < itemsCount(); i++) {
Post post = new Post(
String.format("Post no. %d", i)
);
int j = 0;
post.addComment(new Comment(
String.format(
"Post comment %d:%d", i, j++
)));
post.addComment(new Comment(
String.format(
"Post comment %d:%d", i, j++
)));
session.persist(post);
if(i % batchSize == 0 && i > 0) {
session.flush();
session.clear();
}
}
});
LOGGER.info("{}.testInsert took {} millis",
getClass().getSimpleName(),
TimeUnit.NANOSECONDS.toMillis(
System.nanoTime() - startNanos
));
LOGGER.info("Test batch update");
startNanos = System.nanoTime();
doInTransaction(session -> {
List<Post> posts = session.createQuery(
"select distinct p " +
"from Post p " +
"join fetch p.comments c")
.list();
for(Post post : posts) {
post.title = "Blog " + post.title;
for(Comment comment : post.comments) {
comment.review = "Blog " + comment.review;
}
}
});
LOGGER.in