SELECT语句使用JDBC和Hibernate批量获取
介绍
现在,我已经介绍了Hibernate对INSERT , UPDATE和DELETE语句的批处理支持,是时候分析SELECT语句结果集的批量提取了。
JDBC ResultSet提供了一个客户端代理游标,用于获取当前语句的返回数据。 执行该语句后,必须将结果从数据库游标传输到客户端。 此操作可以立即执行,也可以根据需要执行。
ResultSet游标有三种类型 :
游标类型 | 描述 |
---|---|
TYPE_FORWARD_ONLY | 这是默认的ResultSet游标类型。 结果集只能向前移动,并且结果数据可以一次获取,也可以在迭代游标时检索。 数据库可以决定在查询开始时还是在获取时获取可用的数据。 |
TYPE_SCROLL_INSENSITIVE | 可以向前和向后滚动结果集,并且结果数据对游标仍处于打开状态时发生的并发更改不敏感 |
TYPE_SCROLL_SENSITIVE | 可以向前和向后滚动结果集,并且结果数据对在游标仍处于打开状态时发生的并发更改敏感 。 因此,数据是按需获取的,而不是从数据库游标缓存中获取的 |
并非所有数据库驱动程序都实现所有游标类型,并且批处理获取行为是通过JDBC语句 fetchSize属性控制的,根据Javadoc :
当此Statement生成的ResultSet对象需要更多行时,向JDBC驱动程序提供有关应该从数据库中获取的行数的提示。 如果指定的值为零,则忽略提示。 默认值为零。
因此,默认的获取策略是特定于数据库的,从应用程序性能的角度来看,这方面在调整数据访问层时非常重要:
- 甲骨文 默认情况下, Oracle JDBC运行查询时,它一次从数据库游标中检索10行的结果集。根据Oracle JDBC驱动程序文档 :“合理的”取决于应用程序的详细信息。 Oracle建议fetchSize不超过100,尽管在某些情况下可能更合适。 对于某些查询,即使返回许多行, fetchSize可能也会过大。
- 的MySQL 默认情况下, 结果集是完全检索和存储在内存中。 在大多数情况下,这是最有效的操作方式,并且由于MySQL网络协议的设计,因此更易于实现。
- SQL服务器 通常,当用于SQL Server的Microsoft JDBC驱动程序执行查询时,驱动程序会将所有结果从服务器检索到应用程序内存中。 尽管这种方法最大程度地减少了SQL Server上的资源消耗,但它可以在JDBC应用程序中引发产生非常大结果的查询的OutOfMemoryError 。
- PostgreSQL的 默认情况下,驱动程序会立即收集查询的所有结果。 这对于大型数据集可能很不方便,因此JDBC驱动程序提供了一种将ResultSet基于数据库游标并且仅获取少量行的方法。
- DB2 默认情况下,驱动程序会立即收集查询的所有结果。 这对于大型数据集可能很不方便,因此JDBC驱动程序提供了一种将ResultSet基于数据库游标并且仅获取少量行的方法。 fetchSize属性与queryDataSize属性不同。 fetchSize影响返回的行数,而queryDataSize影响返回的字节数。
例如,如果结果集大小为50 KB,而queryDataSize的值为32767(32KB),则需要两次到数据库服务器的行程才能检索结果集。 但是,如果将queryDataSize设置为65535(64 KB),则只需一趟数据源即可检索结果集。
Java Persistence Query接口通过Query.getResultList()方法调用仅提供全结果检索。
Hibernate还通过其特定的Query.scroll() API支持可滚动的ResultSet游标。
可滚动的ResultSets唯一明显的优点是,由于可以按需获取数据,因此我们可以避免客户端的内存问题。 这听起来似乎是很自然的选择,但实际上,由于以下原因,您不应该获取大型结果集:
- 结果集较大会占用大量数据库服务器资源,并且由于数据库是高度并发的环境 ,因此可能会妨碍可用性和可伸缩性
- 表的大小趋于增长,适度的结果集可能很容易变成很大的表。 这种情况发生在生产系统中,很早就发布了应用程序代码。 由于用户只能浏览整个结果集中的一小部分,因此分页是一种更具可伸缩性的数据提取方法
- 过于常见的偏移分页不适用于大型结果集(因为响应时间随页码线性增加),并且在遍历大型结果集时应考虑键集分页 。 键集分页提供了恒定的响应时间 ,对正在获取的页面的相对位置不敏感
- 即使对于批处理作业 ,将处理项目限制为适当的批处理大小总是比较安全的。 大批量可能导致内存问题或导致长时间运行的事务,从而增加了撤消/重做事务日志的大小
测试时间
我们的域实体模型如下所示:
以下测试将用于验证各种结果集的获取行为:
@Test
public void testFetchSize() {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();}}});long startNanos = System.nanoTime();LOGGER.info("Test fetch size");doInTransaction(session -> {List posts = session.createQuery("select p " +"from Post p " +"join fetch p.comments ").list();LOGGER.info("{}.fetched {} entities",getClass().getSimpleName(),posts.size());});LOGGER.info("{}.testFetch took {} millis",getClass().getSimpleName(),TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos));
}
要将Hibernate配置为使用显式Statement fetchSize ,我们需要设置以下Hibernate属性:
properties.put("hibernate.jdbc.fetch_size", fetchSize());
每个测试将插入5000个Post实体,每个实体具有2个Comment 。
针对商业数据库运行第一个测试,结果如下:
提取大小 | 持续时间[毫秒] |
---|---|
1个 | 1190 |
10 | 640 |
100 | 481 |
1000 | 459 |
10000 | 449 |
默认值(10) | 545 |
提取大小越大,则提取整个结果集所需的往返行程越少。 如果返回的行包含许多列,则较大的访存大小将按比例需要较大的数据库缓冲区。
第二轮测试针对PostgreSQL 9.4运行,结果如下:
提取大小 | 持续时间[毫秒] |
---|---|
1个 | 1181 |
10 | 572 |
100 | 485 |
1000 | 458 |
10000 | 437 |
默认(全部) | 396 |
即使fetchSize等于要返回的总行数,默认的fetch大小也会产生最佳结果。 由于没有上限缓冲区限制,因此在检索大型结果集时,默认的提取大小可能会导致OutOfMemoryError问题。
结论
虽然大多数数据库服务都不会对结果集的获取大小施加默认上限,但是最好限制整个结果集(如果要求允许的话)。 大小有限的结果集应解决无限制的获取大小缺陷,同时即使在查询的数据逐渐增长的情况下,也要确保可预测的响应时间。 查询越短,行级锁被释放的越快,数据访问层的可伸缩性就越高 。
- 代码可在GitHub上获得 。
翻译自: https://www.javacodegeeks.com/2015/04/select-statements-batch-fetching-with-jdbc-and-hibernate.html
SELECT语句使用JDBC和Hibernate批量获取相关推荐
- jdbc select语句_SELECT语句使用JDBC和Hibernate批量获取
jdbc select语句 介绍 现在,我已经介绍了Hibernate对INSERT , UPDATE和DELETE语句的批处理支持,是时候分析SELECT语句结果集的批量提取了. JDBC Resu ...
- hibernate批量查询_使用Hibernate批量获取
hibernate批量查询 如果需要从Java处理大型数据库结果集,则可以选择JDBC,以提供所需的低级控制. 另一方面,如果您已在应用程序中使用ORM,则回退到JDBC可能会带来一些额外的麻烦. 在 ...
- 使用Hibernate批量获取
如果需要从Java处理大型数据库结果集,则可以选择JDBC,以提供所需的低级控制. 另一方面,如果您已在应用程序中使用ORM,则回退到JDBC可能意味着额外的麻烦. 在域模型中导航时,您将失去乐观锁定 ...
- INSERT INTO SELECT语句概述和示例
This article covers the SQL INSERT INTO SELECT statement along with its syntax, examples and use cas ...
- mysql 取左_MySQL select语句从字符串左侧获取5个字符
要从字符串的左侧获取字符数,请在MySQL中使用LEFT方法.让我们首先创建一个表-mysql> create table DemoTable ( Name varchar(100) ); 使用 ...
- hibernate select语句返回的类型
2019独角兽企业重金招聘Python工程师标准>>> Person类中包含有MyEvent这个类 public class Person{private Long id;priva ...
- 用sql的select语句从数据库中获取数据
基本的select语句 select语句中的算数表达式和NULL值 列的别名 使用连接符操作,literal character strings,alternative quote operator, ...
- 使用group by语句时,报错,获取不到数据,出现SELECT list is not in GROUP BY clause and contains nonaggregated column 问题
SELECT list is not in GROUP BY clause and contains nonaggregated column 报错如下: Expression #2 of SELEC ...
- jdbc,mybatis,hibernate各自优缺点及区别
jdbc,mybatis,hibernate各自优缺点及区别 先比较下jdbc编程和hibernate编程各自的优缺点. JDBC: 我们平时使用jdbc进行编程,大致需要下面几个步骤:1,使用jdb ...
最新文章
- php private方法,PHP-private私有访问的操作方法
- MyBatis源码-解读Executor的三个实现类之SimpleExecutor(简单执行器)
- 广州交通大学二年级算法实验题目(第一弹)
- Java31 gt gt gt 2_Java Template.binding方法代碼示例
- Mybatis源码学习笔记
- OpenStack第十四个版本及14项重要事实
- Android学习——android命名规范
- android的Handler、Message机制*
- css3 圣诞红包雨效果
- c语言范式编程之lsearch
- 淘宝类目运营方法 怎么快速获取流量和销量
- java执行bat代码
- Flink DataStream的多流、键控流、窗口、连接、物理分区转换算子的使用
- snaker工作流审批流程参数详解
- 51单片机数码管显示学习笔记
- cad渐开线齿轮轮廓绘制_CAD渐开线齿形怎么绘制
- C语言/C++编程学习:C语言环境设置
- kubernetes二进制安装
- vue.js bootstrap 下拉列表_Excel下拉菜单制作的小技巧
- 专升本——主从复合句
热门文章
- ssm(Spring+Spring mvc+mybatis)Dao层实现类——DeptDaoImpl
- JSP 获得服务器时间和浏览器时间
- 二叉排序树的删除+图解
- 文章中文字乱码问题解决办法集合
- 简单解决“无法打开内核设备:\\Global\\vmx86”错误
- mysql切换用户sql语句,MySQL用户管理及SQL语句详解
- python中debug有什么用途_史上最方便的Python Debug工具
- es嵌套聚合dsl(求均值,求和)
- 自定义类加载器(ClassLoader + URLClassLoader)
- Flex布局 让你的布局更完美