`

JAP2.1 效率优化

阅读更多
经常在网上看到开发者们抱怨 JPA 性能低下的帖子或文章,但如果仔细查看这些性能问题,常会发现导致问题的根本原因大致包括以下几个:

使用过多的 SQL 查询从数据库中获取所需的实体信息,即我们常说的n+1查询问题

逐个更新实体,而不是使用单条语句进行更新

使用 Java 应用程序而非数据库进行大量数据处理

JPA2.1 中三个提升应用性能的新功能 技术分享

JPA提供了处理这类问题的方法,并给 JPA2.1 增加了一些额外功能,可以极大地提升性能表现,笔者将在本文中解释如何利用 JPA2.1 的功能避免上述问题。

顺便提一下,如果想了解Java项目中更多的典型性能问题,可以参考笔者最近发布的基于性能调查结果的深度报告,如果你在寻找 JPA 资源,点击此链接便可获取JPA2.1特征的备忘清单。接下来我们来看看如何用JPA来解决现有的性能问题。

解决「SQL 查询过多」的问题
根据以往的经验,使用过多的 SQL 查询获取所要求的实体是导致性能问题最普遍的原因。

即使是看起来最简单的查询,如果操作不当,也会触发几十次甚至上百次的 SQL 查询。而且,你在本节中可以看到,这类不当操作不一定会出现在查询语句中,而可能只是几个配置不当的注解。所以,如果你觉得这个问题不会造成影响,请三思。

如果在你的项目中出现以下几段代码,你会怎么想?

Java
List authors = this.em.createQuery("SELECT a FROM Author a", 
        Author.class).getResultList();

for (Author a : authors) { 
    System.out.println("作者 "
            + a.getFirstName()
            + " "
            + a.getLastName()
            + " 书籍信息 "
            + a.getBooks()
                    .stream()
                    .map(b -> b.getTitle() + "("
                            + b.getReviews().size() + " 评论)")
                    .collect(Collectors.joining(", ")));
}
上面的代码段会打印所有作者的姓名及其书名,看起来非常简单,但你是否想过它给数据库发送了多少次查询?一次?还是两次?或者 Author、Book、Review 实体各一次?

实际上,这取决于数据库中作者的人数。如果数据库较小,里面只有11名作者和6本书。那么这段代码会触发12次查询,其中1次用于获取所有作者姓名,另外11次给每位作者匹配书名。这一问题被称作 n+1 查询问题,无论我们使用的是 MySQL、SqlServer 还是其他数据库,都容易出现此类问题。因此在生产环境中,随着数据量不断增大,代码的性能就越差。

我们可以通过多种方法,用一次查询获取所有要求的实体信息 ,从而避免这一情况。在笔者看来,使用@NamedEntityGraph 来解决此问题是最新,也最好的方法。

实体图通过独立于查询的方法指定应该从数据库中获取的实体的图。这意味着,你需要为实体图创建一个独立的定义,并在需要时与查询合并。下段代码展示了如何定义根据作者名提取书名的@NamedEntityGraph。

Java
@Entity
@NamedEntityGraph(name = "graph.AuthorBooks", attributeNodes = @NamedAttributeNode("books"))
public class Author implements Serializable { 

}
现在,实体管理器可以用这个图为参考,通过一次查询获取所有作者和书名。在图的定义中可以看到,笔者只提供了包含相关实体的属性名称。因此,笔者将@NamedEntityGraph作为loadgraph (负载图),这样便可提取其他所有属性及其定义的获取类型,如下所示:

Java
EntityGraph graph = this.em.getEntityGraph("graph.AuthorBooks");

List authors = this.em 
.createQuery("SELECT DISTINCT a FROM Author a", Author.class)
.setHint("javax.persistence.loadgraph", graph).getResultList();
该示例展示了一个非常简单的实体图,在实际的应用中,很可能会用到更复杂的图,但这也不成问题。你可以定义多个 @NamedAttributeNodes 以定义更复杂的图,也可以用 @NamedSubGraph 注解来创建多层次的图。如果想了解更多关于 @NamedEntityGraphs 的信息,请点击实体图使用方式详解。

在某些使用案例中,你可能还需要用更动态的方式来定义实体图,比如,根据一些输入参数进行定义。在此类案例中,通过 Java API 用编程的方式定义实体图效果更佳。

解决「逐个更新实体」的问题
逐个更新实体是造成 JPA 性能问题的另一个常见原因。作为 Java 开发者,我们习惯处理对象,并用面向对象的方式思考问题。尽管这是实现复杂逻辑和应用的好方法,但也是处理数据库时导致性能退化的一个常见原因。

从面向对象的角度来看,对实体进行更新和删除操作是完全可以接受的。但当你不得不更新一大组实体时,这种操作就会非常低效。持久性提供者(Persistence Provider)将为每个更新实体创建一个更新语句,并在下一次 flush 操作时发送至数据库中。

然而,SQL 提供了一个更为高效的方式。它允许你创建可一次性更新多个实体的更新语句。你还可以对 JPA 2.1 引入的 CriteriaUpdate 和 CriteriaDelete 语句进行同样的操作。

如果你之前用过 criteria 条件查询,肯定对新的 CriteriaUpdate 以及 CriteriaDelete 语句非常熟悉,更新和删除操作的创建方式几乎与 JPA 2.0 中引入的 criteria 条件查询创建方式一样。

在下面的代码段中可以看到,你需要从实体管理器中获取 CriteriaBuilder 并用它创建 CriteriaUpdate对象,对 CriteriaQuery 进行的操作与此类似,主要区别在于用于定义更新操作的 set 方法。

Java
CriteriaBuilder cb = this.em.getCriteriaBuilder(); 
// create update
CriteriaUpdate update = cb.createCriteriaUpdate(Author.class); 
// set the root class
Root a = update.from(Author.class); 
// set update and where clause
update.set(Author_.firstName, cb.concat(a.get(Author_.firstName), " - updated")); 
update.where(cb.greaterThanOrEqualTo(a.get(Author_.id), 3L));

// perform update
Query q = this.em.createQuery(update); 
q.executeUpdate(); 
在 CriteriaDelete 操作中,你只需要在实体管理器中调用 createCriteriaDelete 方法以获取CriteriaDelete 对象,并用它来定义与上例类似的 FROM 和 WHERE 查询部分。

在数据库中处理数据
作为 Java 开发者,我们倾向于在 Java 中实现所有的应用逻辑,这也是造成性能问题的一大常见原因。别误会,在 Java 中实现逻辑的好处很多,但如果将部分逻辑实现在数据库中,只把结果发送到业务逻辑层,也能得到很好的效果。

在数据库中执行逻辑的方法很多。只用 SQL 语句,也能完成很多事情,如果不够,你还可以调用数据库的特定功能和存储过程。在本文中,笔者将仔细探讨存储过程,更确切地说是探讨调用存储过程的方式。

在 JPA 2.0 中,并没有针对存储过程的实际支持,本地查询是调用存储过程的唯一方式。JPA 2.1.引入了@NamedStoredProcedureQuery 和更为动态的 StoredProcedureQuery,改变了这一现状。在本文中,笔者将重点关注基于注解的、用 @NamedStoredProcedureQuery 进行调用的存储过程的定义。笔者在自己的博客中详细介绍了动态存储过程查询 。

在下面代码段中可以看到, @NamedStoredProcedureQuery 的定义非常简洁,你需要指定查询的名称、数据库中的存储过程名称以及输入和输出参数。在本例中,笔者用输入参数 x 和 y 调用存储过程calculate,期望的输出参数为 sum,其它支持的参数类型还有用于输入和输出的参数 INPUT 和用于检索结果集的 REF_COURSOR。

Java
@NamedStoredProcedureQuery(
name = "calculate", 
procedureName = "calculate", 
parameters = { 
@StoredProcedureParameter(mode = ParameterMode.IN, type = Double.class, name = "x"),
@StoredProcedureParameter(mode = ParameterMode.IN, type = Double.class, name = "y"),
@StoredProcedureParameter(mode = ParameterMode.OUT, type = Double.class, name = "sum") })
@NamedStoredProcedureQuery 的使用方法与 @NamedQuery 相似,你需要向实体管理器的createNamedStoredProcedureQuery 方法提供查询名称,以便在本次查询中获取 StoredProcedureQuery对象,然后,用 setParameter 方法设定输入参数,之后再用 execute 方法调用存储过程。

Java
StoredProcedureQuery query = this.em.createNamedStoredProcedureQuery("calculate"); 
query.setParameter("x", 1.23d); 
query.setParameter("y", 4.56d); 
query.execute(); 
Double sum = (Double) query.getOutputParameterValue("sum"); 
总结
JPA 给数据库存储和检索带来诸多便利。通过这一工具,可快速开展项目,解决大部分问题,但也更容易导致实现非常低效的持久层。由此,普遍存在的问题包括:使用过多查询获取所需数据、逐个更新实体以及在 Java 中执行所有逻辑。

JPA 2.1规范引入了几个新的功能以应对这些低效操作,比如实体图(entity graphs),条件更新(criteria update)和存储过程查询(stored procedure queries)。笔者的JPA2.1新功能备忘单囊括了JPA 2.1的这些功能及其他新功能,你可以免费下载。

(编译自:http://zeroturnaround.com/rebellabs/three-jpa-2-1-features-that-will-boost-your-applications-performance/)
分享到:
评论

相关推荐

    jap.rar_jap简单教程

    【标题】"jap.rar_jap简单教程"指的是一个关于JAP(可能是Java Application Project)的压缩包资源,其中包含了学习和理解JSP(JavaServer Pages)基础应用的材料。这个教程面向的是初学者,旨在帮助他们入门Web开发...

    JAVA JAP分页写法大全

    在Java编程语言中,"JAP分页"通常指的是Java应用程序中的分页查询技术,用于在大量数据中实现高效的检索和展示。这个压缩包文件名"JAVA JAP分页写法大全...记得在实践中注意优化和错误处理,以确保程序的稳定性和效率。

    JAP 简介 入门知识

    JAP 简介 入门知识

    jap网络通讯录addressBook

    这些更新可能包括新功能的添加、性能优化以及安全性的提升,确保用户始终能获得最佳的使用体验。 总的来说,`jap网络通讯录addressBook`是一款功能强大、安全可靠的网络通讯录管理工具,它结合了传统通讯录的实用性...

    网络在线考试jap+struts

    网络在线考试jap+struts网络在线考试jap+struts网络在线考试jap+struts网络在线考试jap+struts网络在线考试jap+struts网络在线考试jap+struts网络在线考试jap+struts网络在线考试jap+struts

    java 动态菜单 jap vs html

    然而,过度依赖JavaScript可能对搜索引擎优化(SEO)不利,且对于不支持JavaScript的浏览器,动态菜单可能无法正常工作。 3. JAP(Java Applet)与HTML动态菜单: 标题中提到的"jap"可能是Java Applet的缩写,这是...

    jap+hibernate4实现

    **JPA(Java Persistence API)与Hibernate 4的结合使用** 在Java开发中,对象关系映射(ORM)框架如Hibernate极大地简化了...在实际项目中,不断调整和优化配置,以适应特定的需求和性能要求,是提升系统效率的关键。

    jap页面jap页面

    通过遵循标准的JavaBean规范,开发者可以创建可扩展和可组合的组件,促进代码的复用,提高开发效率。总的来说,JavaBean是Java Web开发中的核心元素,用于数据管理和业务逻辑的封装,与JSP等技术紧密集成,实现了...

    struts 2.1.8 spring 2.5 jap ext 整合 例子2

    struts 2.1.8 spring 2.5 jap ext 整合 例子 struts 2.1.8 spring 2.5 jap ext 整合 例子

    jap调用应用程序

    ### jap调用应用程序 #### 一、引言 在现代软件开发中,特别是在Web应用领域,JSP(Java Server Pages)技术被广泛应用于构建动态网页。然而,在某些情况下,Java程序可能需要与其他非Java应用程序进行交互,以...

    jap新闻发布系统(网盟jsp新闻发布系统 v0.9)

    在"jap新闻发布系统(网盟jsp新闻发布系统 v0.9)"中,JSP被用作后端服务器端的技术,负责处理用户请求、动态生成网页内容。 【新闻发布系统】 新闻发布系统是一种常见的网站应用,用于发布、管理及展示各种类型的...

    jap+sturs教材管理系统

    "jap+sturs教材管理系统"是一个基于Java技术栈,主要...这样的系统不仅提高了教材管理的效率,也为教学活动提供了便利。对于学习者来说,这是一个绝佳的实践平台,可以深入理解Java Web开发中的核心技术和最佳实践。

    jap-master.zip

    **JAP 中间件详解与应用** JAP(Just Another Proxy),是一款强大的开源登录认证中间件,其核心设计理念是模块化,旨在为各种WEB应用提供标准化的登录认证服务。通过JAP,开发者能够轻松地集成登录认证功能到自己...

    基于JAP的网上考试系统的设计与实现带数据库

    合理的数据库设计能够优化查询效率,保证数据的一致性和完整性。例如,试题表可能包含试题ID、题目、选项、正确答案等字段;用户表则记录用户ID、用户名、密码、联系方式等信息。 五、安全与优化 1. 安全性:采用...

    JAP编程学习100例

    顺序如下: 1、多种字体大小显示 2、c:out标记输出 3、获取当前时间 4、include包含语句 5、建立错误处理页面的范例程序 6、jsp:forward 7、简单计数器 8、设置页面属性 9、使用GB2312编码 10、使用Big5编码 ...

    书店管理系统(jap+sqlServer)

    【书店管理系统(jap+sqlServer)】是一个基于JSP技术开发的网上书店管理平台,其核心功能包括图书管理、用户交互以及订单处理等。这个系统利用了Java Servlet(JSP的一部分)作为服务器端编程语言,与Microsoft SQL...

    jap的PageBean分页类

    jsp的PageBean分装,内有写好的page方法,传入参数即可方法基于hibernate和struts

    jap wap 开发实例

    3. **数据传输优化**:由于无线网络带宽有限,开发者需要考虑如何有效地传输和展示数据。可能涉及的技术有数据压缩、缓存策略以及最小化HTTP请求等。 4. **用户界面设计**:WML的卡片式布局需要特别注意用户体验,...

    Spring mvc +jap

    标题中的"Spring MVC + JAP"指的是Spring Model-View-Controller框架与Java Persistence API的集成应用。Spring MVC是Spring框架的一部分,主要用于构建Web应用程序的后端控制层,提供了一个灵活的MVC架构,使得...

Global site tag (gtag.js) - Google Analytics