介绍

Hibernate简化了CRUD操作,尤其是在处理实体图时。 但是任何抽象都有其代价,而Hibernate也不例外。 我已经讨论了获取策略和了解Criteria SQL查询的重要性,但是您可以做更多的事情来统治JPA。 这篇文章是关于控制Hibernate代表您调用的SQL语句计数的。

在ORM工具如此流行之前,所有数据库交互都是通过显式SQL语句完成的,而优化主要针对慢速查询。

Hibernate可能给人一种错误的印象,即您不必担心SQL语句。 这是一个错误和危险的假设。 Hibernate应该减轻域模型的持久性,而不是使您摆脱任何SQL交互。

使用Hibernate,您可以管理实体状态转换,然后转换为SQL语句。 生成的SQL语句的数量受当前的获取策略,条件查询或集合映射影响,您可能并不总是能获得所需的结果。 忽略SQL语句是有风险的,最终可能会给整个应用程序性能带来沉重的负担。

我是同行评审的坚定倡导者,但这并不是发现不良的Hibernate使用情况的“必要条件”。 细微的更改可能会影响SQL语句的计数,并且在审核过程中不会引起注意。 同样重要的是,当“猜测” JPA SQL语句时,我觉得我可以使用任何其他帮助。 我要尽可能地实现自动化,这就是为什么我想出一种用于执行SQL语句计数期望的机制的原因。

首先,我们需要一种方法来拦截所有已执行的SQL语句。 我研究了这个主题,很幸运能找到这个出色的数据源代理库。

添加自动验证器

此保护措施旨在仅在测试阶段运行,因此我将其专门添加到“集成测试”弹簧上下文中。 我已经讨论过Spring bean的别名 ,这是使用它的正确时机。

<bean id="testDataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init"destroy-method="close"><property name="className" value="bitronix.tm.resource.jdbc.lrc.LrcXADataSource"/><property name="uniqueName" value="testDataSource"/><property name="minPoolSize" value="0"/><property name="maxPoolSize" value="5"/><property name="allowLocalTransactions" value="false" /><property name="driverProperties"><props><prop key="user">${jdbc.username}</prop><prop key="password">${jdbc.password}</prop><prop key="url">${jdbc.url}</prop><prop key="driverClassName">${jdbc.driverClassName}</prop></props></property>
</bean><bean id="proxyDataSource" class="net.ttddyy.dsproxy.support.ProxyDataSource"><property name="dataSource" ref="testDataSource"/><property name="listener"><bean class="net.ttddyy.dsproxy.listener.ChainListener"><property name="listeners"><list><bean class="net.ttddyy.dsproxy.listener.CommonsQueryLoggingListener"><property name="logLevel" value="INFO"/></bean><bean class="net.ttddyy.dsproxy.listener.DataSourceQueryCountListener"/></list></property></bean></property>
</bean><alias name="proxyDataSource" alias="dataSource"/>

新的代理数据源将装饰现有数据源,从而拦截所有已执行的SQL语句。 该库可以记录所有SQL语句以及实际参数值,这与默认的Hibernate记录不同,该记录只显示一个占位符。

验证器的外观如下:

public class SQLStatementCountValidator {private SQLStatementCountValidator() {}/*** Reset the statement recorder*/public static void reset() {QueryCountHolder.clear();}/*** Assert select statement count* @param expectedSelectCount expected select statement count*/public static void assertSelectCount(int expectedSelectCount) {QueryCount queryCount = QueryCountHolder.getGrandTotal();int recordedSelectCount = queryCount.getSelect();if(expectedSelectCount != recordedSelectCount) {throw new SQLSelectCountMismatchException(expectedSelectCount, recordedSelectCount);}}/*** Assert insert statement count* @param expectedInsertCount expected insert statement count*/public static void assertInsertCount(int expectedInsertCount) {QueryCount queryCount = QueryCountHolder.getGrandTotal();int recordedInsertCount = queryCount.getInsert();if(expectedInsertCount != recordedInsertCount) {throw new SQLInsertCountMismatchException(expectedInsertCount, recordedInsertCount);}}/*** Assert update statement count* @param expectedUpdateCount expected update statement count*/public static void assertUpdateCount(int expectedUpdateCount) {QueryCount queryCount = QueryCountHolder.getGrandTotal();int recordedUpdateCount = queryCount.getUpdate();if(expectedUpdateCount != recordedUpdateCount) {throw new SQLUpdateCountMismatchException(expectedUpdateCount, recordedUpdateCount);}}/*** Assert delete statement count* @param expectedDeleteCount expected delete statement count*/public static void assertDeleteCount(int expectedDeleteCount) {QueryCount queryCount = QueryCountHolder.getGrandTotal();int recordedDeleteCount = queryCount.getDelete();if(expectedDeleteCount != recordedDeleteCount) {throw new SQLDeleteCountMismatchException(expectedDeleteCount, recordedDeleteCount);}}
}

该实用程序与JPA和MongoDB乐观并发控制重试机制一起,是我的db-util项目的一部分。

由于它已经在Maven Central Repository中提供,因此您只需将以下依赖项添加到pom.xml中就可以轻松使用它:

<dependency><groupId>com.vladmihalcea</groupId><artifactId>db-util</artifactId><version>0.0.1</version>
</dependency>

让我们写一个测试来检测臭名昭著的N + 1选择查询问题 。

为此,我们将编写两种服务方法,其中一种受到上述问题的影响:

@Override
@Transactional
public List<WarehouseProductInfo> findAllWithNPlusOne() {List<WarehouseProductInfo> warehouseProductInfos = entityManager.createQuery("from WarehouseProductInfo", WarehouseProductInfo.class).getResultList();navigateWarehouseProductInfos(warehouseProductInfos);return warehouseProductInfos;
}@Override
@Transactional
public List<WarehouseProductInfo> findAllWithFetch() {List<WarehouseProductInfo> warehouseProductInfos = entityManager.createQuery("from WarehouseProductInfo wpi " +"join fetch wpi.product p " +"join fetch p.company", WarehouseProductInfo.class).getResultList();navigateWarehouseProductInfos(warehouseProductInfos);return warehouseProductInfos;
}private void navigateWarehouseProductInfos(List<WarehouseProductInfo> warehouseProductInfos) {for(WarehouseProductInfo warehouseProductInfo : warehouseProductInfos) {warehouseProductInfo.getProduct();}
}

单元测试非常简单,因为它遵循与任何其他JUnit断言机制相同的编码样式。

try {SQLStatementCountValidator.reset();warehouseProductInfoService.findAllWithNPlusOne();assertSelectCount(1);
} catch (SQLSelectCountMismatchException e) {assertEquals(3, e.getRecorded());
}SQLStatementCountValidator.reset();
warehouseProductInfoService.findAllWithFetch();
assertSelectCount(1);

我们的验证器适用于所有SQL语句类型,因此让我们检查以下服务方法正在执行多少个SQL INSERT:

@Override
@Transactional
public WarehouseProductInfo newWarehouseProductInfo() {LOGGER.info("newWarehouseProductInfo");Company company = entityManager.createQuery("from Company", Company.class).getResultList().get(0);Product product3 = new Product("phoneCode");product3.setName("Phone");product3.setCompany(company);WarehouseProductInfo warehouseProductInfo3 = new WarehouseProductInfo();warehouseProductInfo3.setQuantity(19);product3.addWarehouse(warehouseProductInfo3);entityManager.persist(product3);return warehouseProductInfo3;
}

验证器看起来像:

SQLStatementCountValidator.reset();
warehouseProductInfoService.newWarehouseProductInfo();
assertSelectCount(1);
assertInsertCount(2);

让我们检查一下测试日志,以使自己确信其有效性:

INFO  [main]: o.v.s.i.WarehouseProductInfoServiceImpl - newWarehouseProductInfo
Hibernate: select company0_.id as id1_6_, company0_.name as name2_6_ from Company company0_
INFO  [main]: n.t.d.l.CommonsQueryLoggingListener - Name:, Time:1, Num:1, Query:{[select company0_.id as id1_6_, company0_.name as name2_6_ from Company company0_][]}
Hibernate: insert into WarehouseProductInfo (id, quantity) values (default, ?)
INFO  [main]: n.t.d.l.CommonsQueryLoggingListener - Name:, Time:0, Num:1, Query:{[insert into WarehouseProductInfo (id, quantity) values (default, ?)][19]}
Hibernate: insert into Product (id, code, company_id, importer_id, name, version) values (default, ?, ?, ?, ?, ?)
INFO  [main]: n.t.d.l.CommonsQueryLoggingListener - Name:, Time:0, Num:1, Query:{[insert into Product (id, code, company_id, importer_id, name, version) values (default, ?, ?, ?, ?, ?)][phoneCode,1,-5,Phone,0]}

结论

代码审查是一种很好的技术,但是在大规模开发项目中还远远不够。 这就是为什么自动检查至关重要。 一旦编写了测试,您可以放心,将来的任何更改都不会破坏您的假设。

  • 代码可在GitHub上获得 。
参考: Hibernate事实:如何通过Vlad Mihalcea的Blog博客从我们的JCG合作伙伴 Vlad Mihalcea “断言” SQL语句计数 。

翻译自: https://www.javacodegeeks.com/2014/02/hibernate-facts-how-to-assert-the-sql-statement-count.html

休眠事实:如何“断言” SQL语句计数相关推荐

  1. Hibernate事实:如何“断言” SQL语句计数

    介绍 Hibernate简化了CRUD操作,尤其是在处理实体图时. 但是任何抽象都有其代价,而Hibernate也不例外. 我已经讨论了获取策略和了解Criteria SQL查询的重要性,但是您可以做 ...

  2. 数据库逻辑删除的sql语句_SQL查询优化的数据库设计和逻辑断言

    数据库逻辑删除的sql语句 Database design and Logical Asseveration play a vital role in database performance and ...

  3. sql 语句中count()条件计数

    在count函数里直接对需要计数的变量写条件表达式 但是需要加 'or NULL',如下所示: select count(distinct job_id), count(pay_pv>0 or ...

  4. SQL Server中的SQL语句优化与效率问题

    很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解.比如: select * from table1 where name='zhan ...

  5. SQL语句大全-珍藏首选

    下列语句部分是Mssql语句,不可以在access中使用. SQL分类: DDL-数据定义语言(CREATE,ALTER,DROP,DECLARE) DML-数据操纵语言(SELECT,DELETE, ...

  6. 海量数据库的查询优化及分页算法方案(3)--改善SQL语句[转]

    (从硬盘的某个角落里找到的,也不知道以前是从哪里下载的,在这里对原作者说声抱歉.) 很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解 ...

  7. 面试题: !=!=未看12 略多 sql语句练习 非常 有用

    JAVA面试总结 2015年03月25日 16:53:40 阅读数:4306 刚才看到的,先转载过来,没准以后用到-- 面试总结 2013年8月6日: 问题2:Hibernate的核心接口有哪些?   ...

  8. SQL语句一二三之SQL基本语句

    本文将带领读者掌握SQL四条最基本的数据操作语句:Insert,Select,Update和Delete. 熟练掌握SQL是数据库用户的宝贵财富.在本文中,我们将引导你掌握四条最基本的数据操作语句-S ...

  9. sql语句分析是否走索引_SQL Server 索引使用分析(2)- 改善SQL语句,防止索引失效...

    原文出处 改善SQL语句 很多人不知道SQL语句在sql server中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解.比如: select * from table1 whe ...

最新文章

  1. CloudStack的部署架构概览
  2. 正则表达式二 :贪婪与非贪婪
  3. Springboot本地缓存和redis缓存
  4. 【PAT甲级 StringBuilder的使用】1005 Spell It Right (20 分) Java版 7/7通过
  5. Android之Activity的4种加载模式
  6. C++ 实现布隆过滤器(BloomFilter)
  7. android滑动开关框架,Android之实现滑动开关组件
  8. TCP报文段的首部格式
  9. exchange 日常管理之八:合并用户邮箱
  10. 高性能mysql sakila_《高性能MySQL》读书笔记二
  11. CAVLC和CABAC简介
  12. [UE4]在UI中获取玩家角色实例
  13. 服务器虚拟内存可以设置其他盘,Win7系统如何把虚拟内存设置在其它盘符?
  14. 安工大计算机组成原理实验报告,计算机组成原理实验报告.doc
  15. 酷柚易汛进销存-如何新增付款单?
  16. 英语3500词(七)dating主题(2022.1.19)
  17. Springboot+RSA非对称加密
  18. Ethical.Hacking.2021.10:BUILDING TROJANS
  19. SMS发送WapPush
  20. iocp端口断开_在完成端口IOCP模型判断客户端是否已关闭连接(掉线) | 学步园

热门文章

  1. vue项目没有启动成功的原因之一
  2. 2的负x次幂图像_数学| NO.2,3 函数 T15
  3. 道指mt4代码_剑指offer算法题052:正则表达式匹配
  4. python线性加权模型_局部加权之线性回归(1) - Python实现
  5. spring的PathMatchingResourcePatternResolver基于ant通配符匹配路径遍历项目所有xml文件
  6. 自动配置jdk_JDK 15中自动自动发送更好的NullPointerException消息
  7. java 9 module_Java 9:欢迎来到Module World
  8. log4j2 无日志记录_在Log4j2中更好地执行非日志记录器调用
  9. swarm:pending_WildFly Swarm:使用Java EE构建微服务
  10. 序列化与反序列化的单例模式_序列化代理模式