如何使用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;其底角不再是圆角而是正方形。 在将红色矩形作为内容放置到标题窗…

electron 微信扫码登录 ERR_PROXY_CONNECTION_FAILED

electron: Failed to load URL with error: ERR_PROXY_CONNECTION_FAILED 解决 电脑代理问题;电脑设置里面找到代理,关闭就好了

关于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…

滚动条样式设置

{scrollbar-arrow-color: red; /*上下按钮上三角箭头的颜色*/scrollbar-face-color: #CBCBCB; /*滚动条凸出部分的颜色*/scrollbar-3dlight-color: blue; /*滚动条亮边的颜色*/scrollbar-highlight-color: #333; /*滚动条空白部分的颜色*/scrollbar-shadow-color: yellow; /*滚…

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

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

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

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

JDK 8流和分组

我在JDK 8中的Stream-Powered Collections Functionality中介绍了将JDK 8的Streams与Java集合一起使用的强大功能。 我没有在那篇文章中讨论groupingBy Collector 减少操作的使用&#xff0c;因此在这篇文章中解决了分组问题。 这篇博文中的示例将演示如何将集合支持的流与gro…

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…

我发现我的Java重拍了!

在一月份&#xff0c;我写了一篇文章&#xff0c;介绍了一些我希望在Java语言中看到的变化&#xff0c;这些变化会让我更加喜欢它&#xff08;并使它变得更现代&#xff09;。 很多人建议使用许多JVM语言&#xff0c;但我基本上不予使用&#xff0c;因为这不是我想要的。 我希望…

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。 它…

nullnull使用PL/SQL获取创建用户的语句

在写这篇文章之前&#xff0c;xxx已经写过了几篇关于改nullnull主题的文章,想要了解的朋友可以去翻一下之前的文章 Create procedure: create or replace procedure get_case_sqls_for_ddls_ver1 ascursor get_username isselect username from dba_users;beginfor l_user in g…

axios发送post数据后台收不到_使用axios post 提交数据,后台获取不到

https://www.cnblogs.com/yiyi17/p/9409249.html(copyhttps://www.cnblogs.com/loveyaxin/p/8385694.html(copy问题场景场景很简单&#xff0c;就是一个正常 axios post 请求&#xff1a;axios({headers: {deviceCode: A95ZEF1-47B5-AC90BF3},method: post,url: /api/lockServe…

在JDK 8中连接字符串

JDK 8引入了语言功能&#xff0c;例如lambda表达式 &#xff0c; 流 &#xff0c;甚至是新的Date / Time API &#xff0c;这些都会改变我们编写Java应用程序的方式。 但是&#xff0c;还有一些新的API和功能可能不太“改变游戏规则”&#xff0c;但仍为Java编程语言带来了更大…