hibernate批量查询_使用Hibernate批量获取

hibernate批量查询

如果需要从Java处理大型数据库结果集,则可以选择JDBC,以提供所需的低级控制。 另一方面,如果您已在应用程序中使用ORM,则回退到JDBC可能会带来一些额外的麻烦。 在导航域模型时,您将失去诸如乐观锁定,缓存,自动获取之类的功能。 幸运的是,大多数ORM,例如Hibernate,都有一些选择来帮助您。 虽然这些技术不是新技术,但有两种可能可供选择。

一个简化的例子; 假设我们有一个表(映射到类'DemoEntity'),具有100.000条记录。 每个记录由一个列(映射到DemoEntity中的属性“ property”)组成,其中包含一些大约2KB的随机字母数字数据。

JVM与-Xmx250m一起运行。 假设250MB是可以分配给系统上JVM的总最大内存。 您的工作是读取表中当前的所有记录,进行一些未进一步指定的处理,最后存储结果。 我们假设批量操作产生的实体没有被修改。 首先,我们将首先尝试显而易见的方法,即执行查询以简单地检索所有数据:

new TransactionTemplate(txManager).execute(new TransactionCallback<Void>() {@Overridepublic Void doInTransaction(TransactionStatus status) {Session session = sessionFactory.getCurrentSession();List<DemoEntity> demoEntitities = (List<DemoEntity>) session.createQuery('from DemoEntity').list();for(DemoEntity demoEntity : demoEntitities){//Process and write result}return null;}
});

几秒钟后:

Exception in thread 'main' java.lang.OutOfMemoryError: GC overhead limit exceeded

显然,这不会削减。 为了解决这个问题,我们将切换到Hibernate可滚动结果集,这可能是大多数开发人员都知道的。 上面的示例指示hibernate执行查询,将整个结果映射到实体并返回它们。 使用滚动结果集时,记录一次转换为一个实体:

new TransactionTemplate(txManager).execute(new TransactionCallback<Void>() {@Overridepublic Void doInTransaction(TransactionStatus status) {Session session = sessionFactory.getCurrentSession();ScrollableResults scrollableResults = session.createQuery('from DemoEntity').scroll(ScrollMode.FORWARD_ONLY);int count = 0;while (scrollableResults.next()) {if (++count > 0 && count % 100 == 0) {System.out.println('Fetched ' + count + ' entities');}DemoEntity demoEntity = (DemoEntity) scrollableResults.get()[0];//Process and write result}return null;}
});

运行此后,我们得到:

...
Fetched 49800 entities
Fetched 49900 entities
Fetched 50000 entities
Exception in thread 'main' java.lang.OutOfMemoryError: GC overhead limit exceeded

尽管我们使用的是可滚动的结果集,但每个返回的对象都是一个附加对象,并成为持久性上下文(即会话)的一部分。 结果实际上与我们使用“ session.createQuery('from DemoEntity')。list() '的第一个示例相同。 但是,采用这种方法,我们无法控制。 一切都在幕后发生,如果Hibernate完成了工作,您将获得包含所有数据的列表。 另一方面,使用可滚动的结果集使我们迷上了检索过程,并允许我们在需要时释放内存。 正如我们已经看到的那样,它不会自动释放内存,您必须指示Hibernate实际执行此操作。 存在以下选项:

  • 处理对象后将其从持久性上下文中逐出
  • 偶尔清除整个会话

我们将选择第一个。 在上面的示例的第13行( // Process和write result )下,我们将添加:

session.evict(demoEntity);

重要:

  • 如果您要对实体(或与其有关联的实体进行级联逐出)进行任何修改,请确保在逐出或清除之前刷新会话,否则由于Hibernate的回写而导致的查询将不会发送到数据库
  • 逐出或清除不会将实体从二级缓存中删除。 如果启用了二级缓存并正在使用它,并且还希望将其删除,请使用所需的sessionFactory.getCache()。evictXxx()方法
  • 从您退出实体的那一刻起,该实体将不再附加(不再与会话关联)。 在该阶段对实体所做的任何修改将不再自动反映到数据库中。 如果您使用的是延迟加载,则访问驱逐之前未加载的任何属性都会产生著名的org.hibernate.LazyInitializationException。 因此,基本上,在逐出或清除之前,请确保已完成对该实体的处理(或至少已对其进行初始化以进一步满足需要)

再次运行该应用程序后,我们看到它现在已成功执行:

...
Fetched 99800 entities
Fetched 99900 entities
Fetched 100000 entities

顺便说一句; 您还可以将查询设置为只读,以允许Hibernate执行一些其他优化:

ScrollableResults scrollableResults = session.createQuery('from DemoEntity').setReadOnly(true).scroll(ScrollMode.FORWARD_ONLY);

这样做只会在内存使用方面产生很小的差异,在此特定的测试设置中,它使我们能够在给定的内存量下额外读取约300个实体。 就我个人而言,我不会仅将此功能仅用于内存优化,而仅当它适合您的整体不变性策略时才使用。 使用hibernate,您可以使用不同的选项将实体设置为只读:在实体本身上,整个会话为只读,依此类推。 分别对查询设置只读为false可能是最不推荐的方法。 (例如,之前在会话中加载的实体将保持不受影响,可能可修改。即使查询返回的根对象是只读的,惰性关联也将可修改地加载)。

好的,我们能够处理我们的100.000条记录,生活很好。 但是事实证明,Hibernate对于批量操作还有另一个选择:无状态会话。 您可以从无状态会话中获取可滚动结果集,方法与从普通会话中获取方法相同。 无状态会话直接位于JDBC之上。 Hibernate将在几乎“所有功能禁用”模式下运行。 这意味着没有持久上下文,没有第二级缓存,没有脏检测,没有延迟加载,基本上什么也没有。 从javadoc:

/*** A command-oriented API for performing bulk operations against a database.* A stateless session does not implement a first-level cache nor interact with any * second-level cache, nor does it implement transactional write-behind or automatic * dirty checking, nor do operations cascade to associated instances. Collections are * ignored by a stateless session. Operations performed via a stateless session bypass * Hibernate's event model and interceptors.  Stateless sessions are vulnerable to data * aliasing effects, due to the lack of a first-level cache. For certain kinds of * transactions, a stateless session may perform slightly faster than a stateful session.** @author Gavin King*/

它唯一要做的就是将记录转换为对象。 这可能是一个有吸引力的选择,因为它可以帮助您摆脱手动驱逐/冲洗的麻烦:

new TransactionTemplate(txManager).execute(new TransactionCallback<Void>() {@Overridepublic Void doInTransaction(TransactionStatus status) {sessionFactory.getCurrentSession().doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {StatelessSession statelessSession = sessionFactory.openStatelessSession(connection);try {ScrollableResults scrollableResults = statelessSession.createQuery('from DemoEntity').scroll(ScrollMode.FORWARD_ONLY);int count = 0;while (scrollableResults.next()) {if (++count > 0 && count % 100 == 0) {System.out.println('Fetched ' + count + ' entities');}DemoEntity demoEntity = (DemoEntity) scrollableResults.get()[0];//Process and write result }} finally {statelessSession.close();}}});return null;}
});

除了无状态会话具有最佳的内存使用情况外,使用它还会带来一些副作用。 您可能已经注意到,我们正在打开一个无状态会话并显式关闭它:既没有sessionFactory.getCurrentStatelessSession()也没有(在撰写本文时)任何用于管理无状态会话的Spring集成。打开无状态会话会分配一个新的Java。默认情况下sql.Connection(如果使用openStatelessSession() )执行其工作,因此间接产生第二个事务。 您可以通过使用Hibernate工作API来减轻这些副作用,如提供当前Connection并将其传递给openStatelessSession(Connection connection)的示例中所示。 最后关闭会话对物理连接没有影响,因为它是由Spring基础结构捕获的:打开无状态会话时,仅逻辑连接句柄已关闭,并且创建了新的逻辑连接句柄。

还要注意,您必须自己关闭无状态会话,并且上面的示例仅适用于只读操作。 从您打算使用无状态会话进行修改的那一刻起,还有更多警告。 如前所述,Hibernate模式在“所有功能都已禁用”模式下运行,因此直接导致实体以分离状态返回。 对于您修改的每个实体,您都必须明确地调用: statelessSession.update(entity) 。 首先,我尝试使用此方法来修改实体:

new TransactionTemplate(txManager).execute(new TransactionCallback<Void>() {@Overridepublic Void doInTransaction(TransactionStatus status) {sessionFactory.getCurrentSession().doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {StatelessSession statelessSession = sessionFactory.openStatelessSession(connection);try {DemoEntity demoEntity = (DemoEntity) statelessSession.createQuery('from DemoEntity where id = 1').uniqueResult();demoEntity.setProperty('test');statelessSession.update(demoEntity);} finally {statelessSession.close();}}});return null;}
});

这个想法是我们用现有的数据库Connection打开一个无状态会话。 正如StatelessSession javadoc指示不会发生任何回写一样,我确信无状态会话执行的每个语句都将直接发送到数据库。 最终,当提交事务(由TransactionTemplate开始)时,结果将在数据库中可见。 但是,hibernate使用无状态会话来执行BATCH语句。 我不是100%知道批处理和回写之间有什么区别,但是结果是相同的,因此与javadoc的字典相反,因为语句在以后排队并刷新。 因此,如果您不执行任何特殊操作,则不会刷新批处理的语句,这就是我的情况:“ statelessSession.update(demoEntity);” 被分批处理,从不冲洗。 强制刷新的一种方法是使用Hibernate事务API:

StatelessSession statelessSession = sessionFactory.openStatelessSession();
statelessSession.beginTransaction();
...
statelessSession.getTransaction().commit();
...

在这种情况下,您可能不想仅仅因为使用无状态会话就开始以编程方式控制事务。 此外,由于没有传递我们的Connection,因此我们再次在第二个事务场景中运行无状态会话工作,因此将获得新的数据库连接。 我们无法通过外部连接的原因是,如果我们提交内部事务(“无状态会话事务”),并且它将使用与外部事务相同的连接(由TransactionTemplate开始),则会破坏外部事务事务原子性,因为从外部事务发送到数据库的语句将与内部事务一起提交。 因此,不通过连接意味着打开一个新的连接,从而创建第二笔交易。 更好的选择是触发Hibernate刷新无状态会话。 但是,statelessSession没有“ flush”方法来手动触发刷新。 这里的解决方案是稍微依赖于Hibernate内部API。 该解决方案使手动事务处理和第二个事务处理变得过时:所有语句成为我们(唯一的)外部事务的一部分:

StatelessSession statelessSession = sessionFactory.openStatelessSession(connection);try {DemoEntity demoEntity = (DemoEntity) statelessSession.createQuery('from DemoEntity where id = 1').uniqueResult();demoEntity.setProperty('test');statelessSession.update(demoEntity);((TransactionContext) statelessSession).managedFlush();} finally {statelessSession.close();
}

幸运的是,最近在Spring jira上发布了一个更好的解决方案: https : //jira.springsource.org/browse/SPR-2495这还不是Spring的一部分,但是工厂bean的实现非常简单: StatelessSessionFactoryBean。 java的时候使用这个你可以简单的注入StatelessSession:

@Autowired
private StatelessSession statelessSession;

它将注入一个无状态的会话代理,这等效于正常的“当前”会话的工作方式(稍有不同的是,您注入一个SessionFactory并且每次都需要获取currentSession)。 调用代理时,它将查找绑定到正在运行的事务的无状态会话。 如果已经不存在,它将创建一个与普通会话相同的连接(就像我们在示例中所做的那样),并为无状态会话注册自定义事务同步。 提交事务后,由于同步,将刷新无状态会话,并最终将其关闭。 使用此方法,您可以直接注入无状态会话,并将其用作当前会话(或与注入JPA PeristentContext相同的方式)。 这使您不必处理无状态会话的打开和关闭,而不必处理一种或多种方法以使其变得畅通无阻。 该实现是针对JPA的,但是JPA部分仅限于在getPhysicalConnection()中获得物理连接。 您可以轻松地省略EntityManagerFactory并直接从Hibernate会话获取物理连接。

非常谨慎的结论:最好的方法显然取决于您的情况。 如果您使用普通会话,则在读取或持久存储实体时必须自行解决。 如果您有一个混合事务,那么除了必须手动执行操作之外,还可能影响会话的进一步使用。 你们都在同一笔交易中执行“批量”和“正常”操作。 如果继续进行正常操作,您将在会话中分离实体,这可能会导致意外结果(因为脏检测将不再起作用,依此类推)。 另一方面,您仍将具有主要的Hibernate优势(只要不驱逐该实体),例如延迟加载,缓存,脏检测等。 在编写本文时使用无状态会话需要额外注意管理它(打开,关闭和刷新),这也容易出错。 假设您可以继续使用建议的工厂bean,那么您将拥有一个非常裸露的会话,该会话与正常会话是分开的,但仍参与同一事务。 有了它,您就拥有了一个强大的工具来执行批量操作,而无需考虑内存管理。 缺点是您没有其他可用的Hibernate功能。

参考:在Koen Serneels – Technology博客博客上,从我们的JCG合作伙伴 Koen Serneels 与Hibernate进行批量获取 。

翻译自: https://www.javacodegeeks.com/2013/03/bulk-fetching-with-hibernate.html

hibernate批量查询

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

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

相关文章

Java 9迁移指南:七个最常见的挑战

我确定您已经听说过更新到Java 9并不是一件容易的事&#xff0c;甚至可能是不兼容的更新&#xff0c;而且对于大型代码库而言&#xff0c;迁移毫无意义。 这样做之后&#xff0c;我迁移了一个相当大的旧代码库&#xff0c;我可以告诉你&#xff0c;这还不错。 比碰到Java 8确实…

nuxt sass 全局变量的问题_Sass入门教程

SASS(Syntactically Awesome Stylesheet)是一个CSS预处理器&#xff0c;有助于减少CSS的重复&#xff0c;节省时间。 它是更稳定和强大的CSS扩展语言描述文档的风格结构。sass中文网而且Sass算是CSS的超集&#xff0c;它100%兼容CSS的语法&#xff0c;所有在 CSS 中正常工作的代…

用C语言实现优先级排序和MATLABsort函数的比较

为了实现对两个数组进行优先级排序,用c语言有两种实现方法, 一是需要对两个数组进行排序,然后对排序后的坐标再排序,(求最小值是我自己需要) 二是直接寻找数组排序后的元素坐标,调用qsort函数进行排序,排序后的数组会存放在原数组中,那么就有两种寻找坐标,一是寻找…

造成内存泄漏_如何造成内存泄漏

造成内存泄漏这将是一个相当邪恶的职位-当您确实希望使某人的生活陷入困境时&#xff0c;您将在谷歌上搜索。 在Java开发领域&#xff0c;内存泄漏只是您在这种情况下会引入的错误类型。 为您的受害者保证几天甚至几周的办公室不眠之夜。 我们将在这篇文章中描述两次泄漏。 两…

通过Java,Spring Boot应用程序将Gmail用作SMTP服务器

Gmail用户可以使用Gmail的SMTP服务器smtp.gmail.com从其Spring Boot应用程序发送电子邮件。 为此&#xff0c;让我们在应用程序中进行一些设置&#xff1a; 在application.properties文件中提供SMTP连接属性&#xff1a; spring.mail.hostsmtp.gmail.com spring.mail.username…

在建工地扬尘在线监控系统推荐_配电室为何需要安装蓄电池在线监控系统?保定钰鑫电气...

配电室蓄电池在线监控系统提高了蓄电池运行质量、增强了电力系统的安全运行、保障蓄电池运行环境的可靠&#xff0c;打造无人值守配电室、智能化运维模式&#xff0c;减少蓄电池损耗、浪费&#xff0c;降低了维护成本&#xff0c;为何需要安装一套配电室蓄电池在线监测系统&…

最好的Java开发人员测试和集成工具

通过从应用程序中学习企业APM产品&#xff0c;发现更快&#xff0c;更高效的性能监控。 参加AppDynamics APM导览&#xff01; 无论您是刚开始还是已经使用了一段时间&#xff0c;使用正确的工具进行编程都可以对项目的成功产生巨大的影响。 适当的工具使您可以编写更好的代码…

最速下降法matlab全局最小值_梯度下降概念

1、梯度概念(1)从几何意义上讲&#xff0c;就是函数变化最快的地方。在单变量的函数中&#xff0c;梯度只是导数&#xff0c;其实就是函数在某个给定点的切线的斜率&#xff1b;在多变量函数中&#xff0c;梯度是一个向量&#xff0c;向量有方向&#xff0c;梯度的方向就指出了…

IntelliJ中的远程调试Wildfly应用程序

远程调试Java应用程序意味着使用本地开发环境连接到远程运行的应用程序。 Java开箱即-agentlib:jdwp[options]支持远程调试&#xff1a;目标应用程序必须使用-agentlib:jdwp[options]选项执行&#xff0c; -agentlib:jdwp[options]选项加​​载Java调试线协议&#xff08;jdwp&…

+h eclipse中ctrl_Eclipse 常用的快捷键都有哪些?

今天&#xff0c;小编大概整理了 几 组 Eclipse 的快捷键&#xff0c;希望对你有帮助。1、打开资源CTRL SHIFT R&#xff1a;打开所有类型文件&#xff0c;不包括 JAR 包&#xff1b; CTRL SHIFT T&#xff1a;打开 Java 类型文件&#xff0c;包括 JAR 包&#xff1b;2、查…

apache.camel_Apache Camel 2.11发布

apache.camel上周Apache Camel 2.11发布了。 这篇博客文章总结了最引人注目的新功能和改进。 有关详细说明&#xff0c;请参见Camel 2.11发行说明 。 1&#xff09;新组件 与往常一样&#xff0c;每个新版本都包含许多新组件&#xff0c;这些组件是由我们庞大的用户群贡献的。…

c向文件中插入数据_Redis从文件中批量插入数据

简介在redis中&#xff0c;有时候需要批量执行某些命令&#xff0c;但是在redis的redis-cli下&#xff0c;只能一条条的执行指令&#xff0c;实在太麻烦了&#xff01;想到这&#xff0c;你是不是蓝瘦香菇&#xff1f; 如果能将要执行的指令一行行存储到文件中&#xff0c;然后…

python多级目录import_你真的会用Python模块与工具包吗?

在开发过程中&#xff0c;我们无法把所有代码、资源都放在同一个文件中。因此&#xff0c;模块导入在编码中是很常见的。无论是C、Java&#xff0c;还是Python、Go。可以把不同功能、不同模块进行分离&#xff0c;当使用的时候&#xff0c;可以通过import关键字在一个模块中使用…

八边形点坐标数的lisp_图形学入门第五课:齐次坐标

齐次坐标(Homegeneous Coordinates)在学习齐次坐标之前&#xff0c;我们要先好奇的问一下&#xff0c;为什么要学习齐次坐标。上一节课&#xff0c;我们学习了变换的三种基本形式&#xff1a;旋转&#xff0c;缩放&#xff0c;和切变。但是还有一种特殊的变换&#xff1a;Trans…

对速度的需求,访问现有数据的速度提高了1000倍

了解如何通过使用标准Java 8流和Speedment的In-JVM-Memory加速器将分析数据库应用程序加速1000倍。 Web和移动应用程序有时会很慢&#xff0c;因为后备数据库很慢和/或与数据库的连接施加了延迟。 现代UI和交互式应用程序需要快速后端&#xff0c;并且理想情况下没有可观察到的…

mysqls压力测试怎么用_用 Swagger 测试接口,怎么在请求头中携带 Token?

松哥周末抽空给 Spring Security 系列也录制了一套视频&#xff0c;目录如下&#xff1a;感兴趣的小伙伴戳这里-->Spring BootVue微人事视频教程今天的话题来自一个小伙伴在微信上的提问&#xff1a;看到这个问题&#xff0c;松哥忽然想到我自己之前写过 Spring BootSwagger…

安卓手机背景变黑色怎么改_别着急扔掉旧手机 你的电脑可能需要它

PC玩家中&#xff0c;不少人都会有在玩游戏时观测电脑硬件状态的习惯。比如查看游戏帧数、CPU频率、GPU频率或是温度等。大多数人都是通过第三方软件&#xff0c;在游戏内把监测数据显示到电脑显示屏角落。可就算是在角落&#xff0c;这些数据依旧会阻挡游戏画面&#xff0c;在…

JDeps入门–分析项目的依赖关系

JDeps是Java依赖关系分析工具 &#xff0c;这是一个命令行工具&#xff0c;它处理Java字节码&#xff08;意味着.class文件或包含它们的JAR&#xff09;&#xff0c;并分析类之间静态声明的依赖关系。 可以用各种方式过滤结果&#xff0c;并可以将其汇总到包或JAR级别。 JDeps还…

禅道开源版用户手册_Docker搭建开源版禅道以及项目基本流程介绍

对于自学软件测试的同学来说&#xff0c;经常会遇到这样的困惑&#xff1a;测试用例怎么写&#xff1f;有啥好的模板&#xff1f;缺陷提交的模板是什么样的&#xff1f;bug的生命周期是啥&#xff1f;项目的流程是啥&#xff1f;以上这些困惑&#xff0c;在你仔细看完这篇文章后…

f12 卡 谷歌浏览器_抢券第二课:利用浏览器F12获取优惠券请求链接

抢券第二课为什么迟迟不来呢&#xff1f;因为最近京东没有那种神券需要定点抢购的&#xff0c;我也没法测试我的理论。现在京东的券随时可以领取到&#xff0c;我多没法测试的东西不想就这样欺骗你们。所以今天的第二课我们讲一讲神奇的谷歌浏览器F1201 工具准备一、浏览器这里…