如何使用Hibernate批处理DELETE语句

介绍

在我以前的文章中 ,我解释了批处理INSERT和UPDATE语句所需的Hibernate配置。 这篇文章将继续本主题的DELETE语句批处理。

领域模型实体

我们将从以下实体模型开始:

注释后细节批处理删除

Post实体与Comment具有一对多关联,并且与PostDetails实体具有一对一关系:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "post",orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();@OneToOne(cascade = CascadeType.ALL, mappedBy = "post",orphanRemoval = true, fetch = FetchType.LAZY)
private PostDetails details;

即将进行的测试将针对以下数据进行:

doInTransaction(session -> {int batchSize = batchSize();for(int i = 0; i < itemsCount(); i++) {int j = 0;Post post = new Post(String.format("Post no. %d", i));        post.addComment(new Comment( String.format("Post comment %d:%d", i, j++)));post.addComment(new Comment(String.format("Post comment %d:%d", i, j++)));post.addDetails(new PostDetails());session.persist(post);if(i % batchSize == 0 && i > 0) {session.flush();session.clear();}}
});

休眠配置

正如已经说明 ,以下属性所需的配料INSERT和UPDATE语句:

properties.put("hibernate.jdbc.batch_size", String.valueOf(batchSize()));
properties.put("hibernate.order_inserts", "true");
properties.put("hibernate.order_updates", "true");
properties.put("hibernate.jdbc.batch_versioned_data", "true");

接下来,我们将检查DELETE语句是否也被批处理。

JPA级联删除

因为级联实体状态转换很方便,所以我将证明CascadeType.DELETEJDBC批处理不能很好地混合使用。

以下测试将要进行:

  • 选择一些帖子以及评论帖子 详细信息
  • 删除帖子 ,同时将delete事件传播到CommentsPostDetails
@Test
public void testCascadeDelete() {LOGGER.info("Test batch delete with cascade");final AtomicReference<Long> startNanos = new AtomicReference<>();addDeleteBatchingRows();doInTransaction(session -> {List<Post> posts = session.createQuery("select distinct p " +"from Post p " +"join fetch p.details d " +"join fetch p.comments c").list();startNanos.set(System.nanoTime());for (Post post : posts) {session.delete(post);}});LOGGER.info("{}.testCascadeDelete took {} millis",getClass().getSimpleName(),TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos.get()));
}

运行此测试将给出以下输出:

Query:{[delete from Comment where id=? and version=?][55,0]} {[delete from Comment where id=? and version=?][56,0]} 
Query:{[delete from PostDetails where id=?][3]} 
Query:{[delete from Post where id=? and version=?][3,0]} 
Query:{[delete from Comment where id=? and version=?][54,0]} {[delete from Comment where id=? and version=?][53,0]} 
Query:{[delete from PostDetails where id=?][2]} 
Query:{[delete from Post where id=? and version=?][2,0]} 
Query:{[delete from Comment where id=? and version=?][52,0]} {[delete from Comment where id=? and version=?][51,0]} 
Query:{[delete from PostDetails where id=?][1]} 
Query:{[delete from Post where id=? and version=?][1,0]}

批注Comment DELETE语句,其他实体在单独的数据库往返中删除。

此行为的原因由ActionQueue排序实现给出:

if ( session.getFactory().getSettings().isOrderUpdatesEnabled() ) {// sort the updates by pkupdates.sort();
}
if ( session.getFactory().getSettings().isOrderInsertsEnabled() ) {insertions.sort();
}

虽然介绍了INSERTSUPDATES ,但根本不对DELETE语句进行排序。 仅当所有语句都属于同一数据库表时,才能重新使用JDBC批处理。 当传入语句针对另一个数据库表时,必须释放当前批处理,以便新批处理与当前语句数据库表匹配:

public Batch getBatch(BatchKey key) {if ( currentBatch != null ) {if ( currentBatch.getKey().equals( key ) ) {return currentBatch;}else {currentBatch.execute();currentBatch.release();}}currentBatch = batchBuilder().buildBatch(key, this);return currentBatch;
}

移除孤儿和手动冲洗

一种变通方法是在前进到新的Child关联之前,先手动刷新Hibernate Session,然后解除所有Child实体的关联:

@Test
public void testOrphanRemoval() {LOGGER.info("Test batch delete with orphan removal");final AtomicReference<Long> startNanos = new AtomicReference<>();addDeleteBatchingRows();doInTransaction(session -> {List<Post> posts = session.createQuery("select distinct p " +"from Post p " +"join fetch p.details d " +"join fetch p.comments c").list();startNanos.set(System.nanoTime());posts.forEach(Post::removeDetails);session.flush();posts.forEach(post -> {for (Iterator<Comment> commentIterator = post.getComments().iterator(); commentIterator.hasNext(); ) {Comment comment =  commentIterator.next();comment.post = null;commentIterator.remove();}});session.flush();posts.forEach(session::delete);});LOGGER.info("{}.testOrphanRemoval took {} millis",getClass().getSimpleName(),TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos.get()));
}

这次,所有DELETE语句均已正确批处理:

Query:{[delete from PostDetails where id=?][2]} {[delete from PostDetails where id=?][3]} {[delete from PostDetails where id=?][1]} 
Query:{[delete from Comment where id=? and version=?][53,0]} {[delete from Comment where id=? and version=?][54,0]} {[delete from Comment where id=? and version=?][56,0]} {[delete from Comment where id=? and version=?][55,0]} {[delete from Comment where id=? and version=?][52,0]} {[delete from Comment where id=? and version=?][51,
Query:{[delete from Post where id=? and version=?][2,0]} {[delete from Post where id=? and version=?][3,0]} {[delete from Post where id=? and version=?][1,0]}

SQL级联删除

更好的解决方案是使用SQL级联删除,而不是JPA实体状态传播机制。 这样,我们还可以减少DML语句的数量。 由于Hibernate Session充当事务后写式缓存 ,因此在将实体状态转换与数据库端自动操作混合使用时,我们必须格外谨慎,因为持久性上下文可能无法反映最新的数据库更改。

Post实体一对多 注释关联使用Hibernate特定的@OnDelete批注进行标记,以便自动生成的数据库模式包括ON DELETE CASCADE指令:

@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "post")
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Comment> comments = new ArrayList<>();

生成以下DDL

alter table Comment add constraint 
FK_apirq8ka64iidc18f3k6x5tc5 foreign key (post_id) 
references Post on delete cascade

使用PostDetails实体一对一的Post关联也可以做到这一点

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id")
@MapsId
@OnDelete(action = OnDeleteAction.CASCADE)
private Post post;

以及相关的DDL

alter table PostDetails add constraint 
FK_h14un5v94coafqonc6medfpv8 foreign key (id) 
references Post on delete cascade

CascadeType.ALLorphanRemoval替换为CascadeType.PERSISTCascadeType.MERGE ,因为我们不再希望Hibernate传播实体删除事件。

测试仅删除Post实体。

doInTransaction(session -> {List<Post> posts = session.createQuery("select p from Post p").list();startNanos.set(System.nanoTime());for (Post post : posts) {session.delete(post);}
});

由于只有一个目标表,因此DELETE语句已正确批处理。

Query:{[delete from Post where id=? and version=?][1,0]} {[delete from Post where id=? and version=?][2,0]} {[delete from Post where id=? and version=?][3,0]}

结论

如果INSERTUPDATE语句的批处理只是配置问题,则DELETE语句需要一些其他步骤,这可能会增加数据访问层的复杂性。

  • 代码可在GitHub上获得 。

翻译自: https://www.javacodegeeks.com/2015/03/how-to-batch-delete-statements-with-hibernate.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝点linux_新闻速读 gt; Windows 10 的 Linux 内核将像驱动程序一样由微软更新服务进行更新 | Linux 中国...

本文字数&#xff1a;3252&#xff0c;阅读时长大约&#xff1a;4 分钟导读&#xff1a;• Ubuntu 发行商 Canonical 将参加微软欧洲虚拟开源峰会 • 树莓派支持 Vulkan 最新进展&#xff1a;通过 70000 项测试 • 谷歌浏览器开始隐藏 URL 详细路径&#xff0c;未来地址栏将只显…

struts2-通配符和动态方法调用

通配符举例--BookAction 1 public class BookAction extends ActionSupport {2 3 public String execute() throws Exception {4 System.out.println("BookAction ********** execute()");5 return null;6 }7 /*8 * 显示图书添加页…

JavaFX技巧18:路径剪切

我最近注意到&#xff0c;我致力于ControlsFX项目的PopOver控件无法正确剪切其内容。 当我为FlexCalendarFX框架开发手风琴弹出窗口时&#xff0c;这一点变得显而易见。 每当最后一个标题窗格扩展时&#xff0c;其底角不再是圆角而是正方形。 在将红色矩形作为内容放置到标题窗…

关于erlang的套接字编程

套接字编程即熟悉的Socket编程&#xff0c;根据传输层协议&#xff0c;可分为&#xff1a;UDP协议和TCP协议.下面写一个简单的例子&#xff0c;再重新认识下它&#xff1a; 1.在同一主机节点下启动两个Erlang节点. a).在第一个Erlang节点下&#xff0c;打开端口为1234的UDP套接…

kotlin 添加第一个 集合_Flutter开发必学Dart语法篇之集合操作符函数与源码分析...

简述:在上一篇文章中&#xff0c;我们全面地分析了常用集合的使用以及集合部分源码的分析。那么这一节讲点更实用的内容&#xff0c;绝对可以提高你的Flutter开发效率的函数&#xff0c;那就是集合中常用的操作符函数。这次说的内容的比较简单就是怎么用&#xff0c;以及源码内…

在Java中确定文件类型

以编程方式确定文件的类型可能非常棘手&#xff0c;并且已经提出并实现了许多基于内容的文件标识方法。 Java中有几种可用于检测文件类型的实现&#xff0c;其中大多数很大程度上或完全基于文件的扩展名。 这篇文章介绍了Java中最常见的文件类型检测实现。 本文介绍了几种在Ja…

程序员编程艺术第十一章:最长公共子序列(LCS)问题

程序员编程艺术第十一章&#xff1a;最长公共子序列(LCS)问题 0、前言 程序员编程艺术系列重新开始创作了&#xff08;前十章&#xff0c;请参考程序员编程艺术第一~十章集锦与总结&#xff09;。回顾之前的前十章&#xff0c;有些代码是值得商榷的&#xff0c;因当时的代码只顾…

gateway 过滤器执行顺序_Gateway网关源码解析—路由(1.1)之RouteDefinitionLocator一览...

一、概述本文主要对 路由定义定位器 RouteDefinitionLocator 做整体的认识。在 《Spring-Cloud-Gateway 源码解析 —— 网关初始化》 中&#xff0c;我们看到路由相关的组件 RouteDefinitionLocator / RouteLocator 的初始化。涉及到的类比较多&#xff0c;我们用下图重新梳理下…

ERP开发中应用字符串解析实现界面翻译智能化

ERP中要实现界面多语言的功能&#xff0c;则要对各种情况的字符串进行处理并作出翻译。有些字符串的翻译是有规律可行的&#xff0c;遵循相应的模板模式&#xff0c;解析字符串&#xff0c;可以实现机器翻译的效果。 请看帐套数据库表的设计ADCOMP CREATE TABLE dbo.ADCOMP(REC…

参数详解 复制进程_如何优化PostgreSQL逻辑复制

How to Optimize PostgreSQL Logical Replication逻辑复制( Logical Replication )或 Pglogical 是表级别的复制。两者都是基于 WAL 的复制机制&#xff0c;允许在两个实例之间复制指定表的WAL 。这两个看起来让人迷惑&#xff0c;到底有什么区别呢&#xff1f; Logical Replic…

Android Studio使用说明

声明: 本博客文章原创类别的均为个人原创&#xff0c;版权所有。转载请注明出处: http://blog.csdn.net/ml3947,另外本人的个人博客:http://www.wjfxgame.com。 凌晨的Google I/O大会上&#xff0c;宣布了Android Studio&#xff0c;引起了现场开发者的一片欢呼。那么&#x…

有些窗口底部被任务栏挡住了_开始使用 Tint2 吧,一款 Linux 中的开源任务栏

Tint2 是我们在开源工具系列中的第 14 个工具&#xff0c;它将在 2019 年提高你的工作效率&#xff0c;能在任何窗口管理器中提供一致的用户体验。-- Kevin Sonney每年年初似乎都有疯狂的冲动想提高工作效率。新年的决心&#xff0c;渴望开启新的一年&#xff0c;当然&#xff…

从jHiccup开始

写完“如何在生产中检测和诊断慢速代码”一文后&#xff0c;我受到读者的鼓励&#xff0c;尝试从Azul系统尝试jHiccup 。 去年&#xff0c;我参加了jHiccup的创建者Gil Tene的演讲&#xff0c;探讨了测量延迟的正确方法&#xff0c;其中&#xff0c;他向我们介绍了jHiccup。 它…

华为内部面试题库---(6)

1.在SMP体系结构中&#xff0c;中断亲和性是指将一个或者多个中断绑定到特定CPU core上运行&#xff0c;下列说法错误的是&#xff1a;A.每个硬件设备都会在/proc/irq下有个中断号命令的目录来标志中断亲和性B.IRQ#目录下smp_affinity文件&#xff0c;通过设置CPU位掩码&#x…

基元需要走吗?

我目前正在使用JSF作为视图技术&#xff0c;使用JPA作为持久层的企业应用程序。 它可能是支持bean或服务方法中的某种东西&#xff0c;但令我震惊&#xff1a;是否有充分的理由在企业应用程序中使用原语&#xff1f; 当我开始围绕J2SE 1.2使用Java进行编程&#xff08;或者是J…

输入参数_太实用!输入参数1秒算出功率,这款计算工具又快又准

随着互联网红利的不断加深&#xff0c;到了后期&#xff0c;不断地各种工具开始涌现&#xff0c;方便了很多用户&#xff0c;填补了市场上的很多空白&#xff0c;有生活娱乐类、提高效率类、垂直专业类、系统工具类等等。工业行业作为各行各业的大头&#xff0c;机械化、智能化…

如何编写NetBeans插件

是否想在NetBeans IDE中添加功能或自动执行某些操作&#xff1f; 跟随我们编写您的第一个NetBeans插件。 让我们超越简单的工具栏示例 &#xff0c;创建一个可以自动更新的插件。 该代码基于NetBeans的WakaTime插件 。 我们的示例插件将仅打印Hello World语句&#xff0c;并在…

Spring Batch教程–最终指南

这是Spring批处理教程&#xff0c;它是Spring框架的一部分。 Spring Batch提供了可重用的功能&#xff0c;这些功能对于处理大量记录至关重要&#xff0c;包括日志记录/跟踪&#xff0c;事务管理&#xff0c;作业处理统计信息&#xff0c;作业重新启动&#xff0c;跳过和资源管…

箱式图 添加异常值平均值_什么是脏数据?怎样用箱形图分析异常值?终于有人讲明白了...

导读&#xff1a;数据质量分析是数据挖掘中数据准备过程的重要一环&#xff0c;是数据预处理的前提&#xff0c;也是数据挖掘分析结论有效性和准确性的基础。没有可信的数据&#xff0c;数据挖掘构建的模型将是空中楼阁。数据质量分析的主要任务是检查原始数据中是否存在脏数据…

窗口程序ImageView(仿QQ图片查看器)

近期一直在学习窗口程序之类的问题,下午正好有机会和大家讨论一下. 程序运行截图&#xff1a; 应用方法&#xff1a; 1、直接把图像文件拖到图标上表现 2、通过命令行方式&#xff0c;示例&#xff1a;ImageView.exe "带全路径的图像文件名称" 3、打开ImageView.exe&…