Querydsl vs. JPA Criteria – Querydsl 与 JPA 标准对比

最后修改: 2024年 3月 19日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Querydsl and JPA Criteria are popular frameworks for constructing type-safe queries in Java. They both provide ways to express queries statically typed, making it easier to write efficient and maintainable code for interacting with databases. In this article, we’ll compare them from various perspectives.

QuerydslJPA Criteria 是在 Java 中构建类型安全查询的流行框架。它们都提供了以静态类型表达查询的方法,从而使编写与数据库交互的高效且可维护的代码变得更加容易。在本文中,我们将从不同角度对它们进行比较。

2. Setup

2.设置

First, we need to set up dependencies and configurations for our tests. In all the examples, we’ll use HyperSQL Database:

首先,我们需要为测试设置依赖关系和配置。在所有示例中,我们都将使用 HyperSQL 数据库

<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>2.7.1</version>
</dependency>

We’ll use JPAMetaModelEntityProcessor and JPAAnnotationProcessor to generate metadata for our frameworks. For this purpose, we’ll add maven-processor-plugin with the following configuration:

我们将使用 JPAMetaModelEntityProcessorJPAAnnotationProcessor 为我们的框架生成元数据。为此,我们将添加 maven-processor-plugin 并进行以下配置:

<plugin>
    <groupId>org.bsc.maven</groupId>
    <artifactId>maven-processor-plugin</artifactId>
    <version>5.0</version>
    <executions>
        <execution>
            <id>process</id>
            <goals>
                <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
                <processors>
                    <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
                    <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                </processors>
            </configuration>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>6.2.0.Final</version>
        </dependency>
    </dependencies>
</plugin>

Then, let’s configure properties for our EntityManager:

然后,让我们为 EntityManager 配置属性:

<persistence-unit name="com.baeldung.querydsl.intro">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <properties>
        <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
        <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:test"/>
        <property name="hibernate.connection.username" value="sa"/>
        <property name="hibernate.connection.password" value=""/>
        <property name="hibernate.hbm2ddl.auto" value="update"/>
        <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
    </properties>
</persistence-unit>

2.1. JPA Criteria

2.1.初级专业人员标准

To use the EntityManager, we need to specify dependencies for any JPA provider. Let’s choose a Hibernate as the most popular one:

要使用EntityManager,我们需要为任何 JPA 提供程序指定依赖关系。让我们选择最常用的Hibernate

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.2.0.Final</version>
</dependency>

To support code generation functionality, we’ll add the Annotation Processor dependency:

为支持代码生成功能,我们将添加 Annotation Processor 依赖项

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-jpamodelgen</artifactId>
    <version>6.2.0.Final</version>
</dependency>

2.2. Querydsl

2.2 querydsl

Since we’re going to use it with the EntityManager, we still need to include the dependency from the previous section. Additionally, we’ll incorporate Querydsl dependency:

由于我们要将其与 EntityManager 一起使用,因此仍需包含上一节中的依赖关系。此外,我们还将加入 Querydsl 依赖关系

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>5.0.0</version>
</dependency>

To support code generation functionality, we’ll add APT based Source code generation dependency:

为支持代码生成功能,我们将添加基于 APT 的源代码生成依赖关系

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <classifier>jakarta</classifier>
    <version>5.0.0</version>
</dependency>

3. Simple Queries

3.简单查询

Let’s start with the simple queries to one entity without any additional logic. We’ll use the next data model, the root entity will be a UserGroup:

让我们从对一个实体的简单查询开始,无需任何附加逻辑。我们将使用下一个数据模型,根实体将是 用户组

@Entity
public class UserGroup {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToMany(cascade = CascadeType.PERSIST)
    private Set<GroupUser> groupUsers = new HashSet<>();

    // getters and setters
}

In this entity, we’ll establish a many-to-many relationship with a GroupUser:

在此实体中,我们将与 GroupUser 建立多对多关系:

@Entity
public class GroupUser {
    @Id
    @GeneratedValue
    private Long id;

    private String login;

    @ManyToMany(mappedBy = "groupUsers", cascade = CascadeType.PERSIST)
    private Set<UserGroup> userGroups = new HashSet<>();

    @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "groupUser")
    private Set<Task> tasks = new HashSet<>(0);
 
    // getters and setters
}

And, finally, we’ll add a Task entity that relates many-to-one to our User:

最后,我们将添加一个 Task 实体,该实体与我们的 User: 有多对一的关系:</em

@Entity
public class Task {
    @Id
    @GeneratedValue
    private Long id;

    private String description;

    @ManyToOne
    private GroupUser groupUser;
    // getters and setters
}

3.1. JPA Criteria

3.1.初级专业人员标准

Now, let’s select all the UserGroup items from the database:

现在,让我们从数据库中选择所有用户组项:

@Test
void givenJpaCriteria_whenGetAllTheUserGroups_thenExpectedNumberOfItemsShouldBePresent() {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<UserGroup> cr = cb.createQuery(UserGroup.class);
    Root<UserGroup> root = cr.from(UserGroup.class);
    CriteriaQuery<UserGroup> select = cr.select(root);

    TypedQuery<UserGroup> query = em.createQuery(select);
    List<UserGroup> results = query.getResultList();
    Assertions.assertEquals(3, results.size());
}

We created an instance of CriteriaBuilder by calling the getCriteriaBuilder() method of EntityManager. Then, we created an instance of CriteriaQuery for our UserGroup model. After that, we obtained an instance of TypedQuery by calling the EntityManager createQuery() method. By calling the getResultList() method, we retrieved the list of entities from the database. As we can see, the expected number of items is present in the result collection.

我们通过调用 EntityManagergetCriteriaBuilder() 方法创建了 CriteriaBuilder 的实例。然后,我们为 UserGroup 模型创建了 CriteriaQuery 的实例。之后,我们通过调用 EntityManager createQuery() 方法获得了 TypedQuery 的实例。通过调用 getResultList() 方法,我们从数据库中获取了实体列表。我们可以看到,结果集合中出现了预期数量的项目。

3.2. Querydsl

3.2 querydsl

Let’s prepare the JPAQueryFactory instance, which we’ll use to create our queries.

让我们准备 JPAQueryFactory 实例,我们将使用它来创建查询。

@BeforeEach
void setUp() {
    em = emf.createEntityManager();
    em.getTransaction().begin();
    queryFactory = new JPAQueryFactory(em);
}

Now, we’ll perform the same query as in the previous section using Querydsl:

现在,我们将使用 Querydsl 执行与上一节相同的查询:

@Test
void givenQueryDSL_whenGetAllTheUserGroups_thenExpectedNumberOfItemsShouldBePresent() {
    List<UserGroup> results = queryFactory.selectFrom(QUserGroup.userGroup).fetch();
    Assertions.assertEquals(3, results.size());
}

Using the selectFrom() method of the JPAQueryFactory starts building a query for our entity. Then, fetch() retrieves the values from the database into the persistence context. Finally, we’ve achieved the same result, but our query-building process is significantly shorter.

使用 JPAQueryFactoryselectFrom() 方法开始为我们的实体创建查询。然后,fetch() 将值从数据库检索到持久化上下文中。最终,我们获得了相同的结果,但我们的查询构建过程大大缩短了。

4. Filtering, Ordering and Grouping

4.筛选、排序和分组

Let’s delve into a more complex example. We’ll explore how our frameworks handle filtering, sorting, and data aggregation queries.

让我们深入了解一个更复杂的示例。我们将探讨我们的框架如何处理筛选、排序和数据聚合查询

4.1. JPA Criteria

4.1.初级专业人员标准

In this example, we’ll query all the UserGroup entities filtering them using names, which should be in one of two lists. The result we’ll sort by UserGroup name in descending order. Additionally, we’ll aggregate unique IDs per each UserGroup from the result:

在本例中,我们将查询所有 UserGroup 实体,并使用名称对其进行过滤,名称应位于两个列表中的一个。我们将按照 UserGroup 名称以降序对结果进行排序。此外,我们将从结果中汇总每个 UserGroup 的唯一 ID:

@Test
void givenJpaCriteria_whenGetTheUserGroups_thenExpectedAggregatedDataShouldBePresent() {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Object[]> cr = cb.createQuery(Object[].class);
    Root<UserGroup> root = cr.from(UserGroup.class);

    CriteriaQuery<Object[]> select = cr
      .multiselect(root.get(UserGroup_.name), cb.countDistinct(root.get(UserGroup_.id)))
      .where(cb.or(
        root.get(UserGroup_.name).in("Group 1", "Group 2"),
        root.get(UserGroup_.name).in("Group 4", "Group 5")
      ))
      .orderBy(cb.desc(root.get(UserGroup_.name)))
      .groupBy(root.get(UserGroup_.name));

    TypedQuery<Object[]> query = em.createQuery(select);
    List<Object[]> results = query.getResultList();
    assertEquals(2, results.size());
    assertEquals("Group 2", results.get(0)[0]);
    assertEquals(1L, results.get(0)[1]);
    assertEquals("Group 1", results.get(1)[0]);
    assertEquals(1L, results.get(1)[1]);
}

All the base methods here are the same as in the previous JPA Criteria section. Instead of selectFrom(), in this example, we use multiselect(), where we specify all the items that will be returned. We use the second parameter of this method for the aggregated amount of UserGroup IDs. In the where() method, we added filters we’ll apply to our query.

这里的所有基本方法都与前面的 JPA 标准部分相同。在本例中,我们使用了 multiselect(),而不是 selectFrom(),在这里我们指定了将返回的所有项目。我们使用该方法的第二个参数来指定 UserGroup ID 的集合数量。在 where() 方法中,我们添加了将应用于查询的筛选器。

Then we call the orderBy() method, specifying the ordering field and type. Finally, in the groupBy() method, we specify a field that will be our key for aggregated data.

然后,我们调用 orderBy() 方法,指定排序字段和类型。最后,在 groupBy() 方法中,我们指定一个字段作为聚合数据的键。

As we can see, a few UserGroup items were returned. They’re placed in the expected order and the result also contains aggregated data.

我们可以看到,返回了一些 UserGroup 项目。它们按照预期的顺序排列,结果还包含汇总数据。

4.2. Querydsl

4.2 querydsl

Now, let’s make the same query using Querydsl:

现在,让我们使用 Querydsl 进行同样的查询:

@Test
void givenQueryDSL_whenGetTheUserGroups_thenExpectedAggregatedDataShouldBePresent() {
    List<Tuple> results = queryFactory
      .select(userGroup.name, userGroup.id.countDistinct())
      .from(userGroup)
      .where(userGroup.name.in("Group 1", "Group 2")
        .or(userGroup.name.in("Group 4", "Group 5")))
      .orderBy(userGroup.name.desc())
      .groupBy(userGroup.name)
      .fetch();

    assertEquals(2, results.size());
    assertEquals("Group 2", results.get(0).get(userGroup.name));
    assertEquals(1L, results.get(0).get(userGroup.id.countDistinct()));
    assertEquals("Group 1", results.get(1).get(userGroup.name));
    assertEquals(1L, results.get(1).get(userGroup.id.countDistinct()));
}

Aiming for grouping functionality, we’ve replaced the selectFrom() method with two separate methods. In the select() method, we’ve specified the group field and aggregated function. In the from() method, we instruct the query builder on which entity should be applied. Similar to JPA Criteria, where(), orderBy(), and groupBy() are used to describe the filtering, ordering, and fields for grouping.

为了实现分组功能,我们用两个独立的方法取代了 selectFrom() 方法。在 select() 方法中,我们指定了分组字段和聚合函数。在 from() 方法中,我们指示查询生成器应用哪个实体。与 JPA Criteria 类似,where()orderBy()groupBy() 用于描述筛选、排序和分组字段。

Finally, we achieved the same result with a slightly more compact syntax.

最后,我们用略微简洁的语法实现了相同的结果。

5. Complex Queries With JOINs

5.使用 JOIN 的复杂查询

In this example, we’ll create complex queries that join all our entities. The result will contain a list of UserGroup entities with all their related entities.

在本示例中,我们将创建连接所有实体的复杂查询。结果将包含一个 UserGroup 实体及其所有相关实体的列表。

Let’s prepare some data for our tests:

让我们为测试准备一些数据:

Stream.of("Group 1", "Group 2", "Group 3")
  .forEach(g -> {
      UserGroup userGroup = new UserGroup();
      userGroup.setName(g);
      em.persist(userGroup);
      IntStream.range(0, 10)
        .forEach(u -> {
            GroupUser groupUser = new GroupUser();
            groupUser.setLogin("User" + u);
            groupUser.getUserGroups().add(userGroup);
            em.persist(groupUser);
            userGroup.getGroupUsers().add(groupUser);
            IntStream.range(0, 10000)
              .forEach(t -> {
                  Task task = new Task();
                  task.setDescription(groupUser.getLogin() + " task #" + t);
                  task.setUser(groupUser);
                  em.persist(task);
              });
        });
      em.merge(userGroup);
  });

So, in our database, we’ll have three UserGroups, each containing ten GroupUsers. Each GroupUser will have ten thousand Tasks.

因此,在我们的数据库中,我们将有三个用户组,每个用户组包含十个GroupUsers。每个 GroupUser 将有一万个 Tasks.

5.1. JPA Criteria

5.1.初级专业人员标准

Now, let’s make a query using JPA CriteriaBuider:

现在,让我们使用 JPA CriteriaBuider 进行查询:

@Test
void givenJpaCriteria_whenGetTheUserGroupsWithJoins_thenExpectedDataShouldBePresent() {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<UserGroup> query = cb.createQuery(UserGroup.class);

    query.from(UserGroup.class)
      .<UserGroup, GroupUser>join(GROUP_USERS, JoinType.LEFT)
      .join(tasks, JoinType.LEFT);

    List<UserGroup> result = em.createQuery(query).getResultList();
    assertUserGroups(result);
}

We use the join() method specifying the entity we want to join with and its type. After the execution, we retrieved a list of results. Let’s assert it using the following code:

我们使用join()方法指定了要连接的实体及其类型。执行完成后,我们将获得一个结果列表。让我们使用下面的代码对其进行断言:

private void assertUserGroups(List<UserGroup> userGroups) {
    assertEquals(3, userGroups.size());
    for (UserGroup group : userGroups) {
        assertEquals(10, group.getGroupUsers().size());
        for (GroupUser user : group.getGroupUsers()) {
            assertEquals(10000, user.getTasks().size());
        }
    }
}

As we can see, all the expected items were retrieved from the database.

我们可以看到,所有预期项目都从数据库中检索到了。

5.2. Querydsl

5.2 querydsl

Let’s achieve the same goal using Querydsl:

让我们使用 Querydsl 来实现同样的目标:

@Test
void givenQueryDSL_whenGetTheUserGroupsWithJoins_thenExpectedDataShouldBePresent() {
    List<UserGroup> result = queryFactory
      .selectFrom(userGroup)
      .leftJoin(userGroup.groupUsers, groupUser)
      .leftJoin(groupUser.tasks, task)
      .fetch();

    assertUserGroups(result);
}

Here, we use the leftJoin() method to add the connection to another entity. All the join types have separate methods. Both syntaxes are not very wordy. In the Querydsl implementation, our query is slightly more readable.

在这里,我们使用 leftJoin() 方法将连接添加到另一个实体。所有连接类型都有单独的方法。两种语法的字数都不多。在 Querydsl 实现中,我们的查询可读性稍高一些。

6. Modifying Data

6. 修改数据

Both frameworks support data modification. We can utilize it to update data based on complex and dynamic criteria. Let’s see how it works.

这两个框架都支持数据修改。我们可以利用它来根据复杂和动态的标准更新数据。让我们看看它是如何工作的。

6.1. JPA Criteria

6.1.初级专业人员标准

Let’s update UserGroup with a new name:

让我们用新名称更新 UserGroup

@Test
void givenJpaCriteria_whenModifyTheUserGroup_thenNameShouldBeUpdated() {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaUpdate<UserGroup> criteriaUpdate = cb.createCriteriaUpdate(UserGroup.class);
    Root<UserGroup> root = criteriaUpdate.from(UserGroup.class);
    criteriaUpdate.set(UserGroup_.name, "Group 1 Updated using Jpa Criteria");
    criteriaUpdate.where(cb.equal(root.get(UserGroup_.name), "Group 1"));

    em.createQuery(criteriaUpdate).executeUpdate();
    UserGroup foundGroup = em.find(UserGroup.class, 1L);
    assertEquals("Group 1 Updated using Jpa Criteria", foundGroup.getName());
}

To modify the data we use a CriteriaUpdate instance, which is used to create a Query. We set all the field names and values are going to be updated. Finally, we call the executeUpdate() method to run the update query. As we can see, we have a modified name field in the updated entity.

要修改数据,我们使用 CriteriaUpdate 实例,该实例用于创建 Query。我们设置要更新的所有字段名称和值。最后,我们调用 executeUpdate() 方法运行更新查询。正如我们所看到的,在更新的实体中,我们修改了名称字段。

6.2. Querydsl

6.2 querydsl

Now, let’s update UserGroup using Querydsl:

现在,让我们使用 Querydsl 更新 UserGroup:

@Test
void givenQueryDSL_whenModifyTheUserGroup_thenNameShouldBeUpdated() {
    queryFactory.update(userGroup)
      .set(userGroup.name, "Group 1 Updated Using QueryDSL")
      .where(userGroup.name.eq("Group 1"))
      .execute();

    UserGroup foundGroup = em.find(UserGroup.class, 1L);
    assertEquals("Group 1 Updated Using QueryDSL", foundGroup.getName());
}

From queryFactory we create the update query by calling the update() method. Then, we set new values for the entity fields using the set() method. We’ve updated the name successfully. Similar to previous examples, Querydsl offers a slightly shorter and more declarative syntax.

queryFactory 中,我们通过调用 update() 方法创建更新查询。然后,我们使用 set() 方法为实体字段设置新值。我们已经成功更新了名称。与之前的示例类似,Querydsl 提供了一种略微简短和更具声明性的语法。

7. Integration With Spring Data JPA

7.与 Spring Data JPA 集成

We can use Querydsl and JPA Criteria to implement dynamic filtering in our Spring Data JPA repositories. Let’s add Spring Data JPA starter dependency first:

我们可以使用 Querydsl 和 JPA Criteria 在 Spring Data JPA 资源库中实现动态筛选。让我们先添加 Spring Data JPA starter 依赖关系:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>3.2.3</version>
</dependency>

7.1. JPA Criteria

7.1.初级专业人员标准

Let’s create a Spring Data JPA repository for our UserGroup extending JpaSpecificationExecutor:

让我们为我们的 UserGroup 扩展 JpaSpecificationExecutor 创建一个 Spring Data JPA 存储库:

public interface UserGroupJpaSpecificationRepository 
  extends JpaRepository<UserGroup, Long>, JpaSpecificationExecutor<UserGroup> {

    default List<UserGroup> findAllWithNameInAnyList(List<String> names1, List<String> names2) {
        return findAll(specNameInAnyList(names1, names2));
    }

    default Specification<UserGroup> specNameInAnyList(List<String> names1, List<String> names2) {
        return (root, q, cb) -> cb.or(
          root.get(UserGroup_.name).in(names1),
          root.get(UserGroup_.name).in(names2)
        );
    }
}

In this repository, we’ve created a method that filters our results based on any of two name lists from the parameters. Let’s use it and see, how it works:

在该版本库中,我们创建了一个方法,该方法可根据参数中的两个名称列表中的任意一个过滤结果。让我们使用它,看看它是如何工作的:

@Test
void givenJpaSpecificationRepository_whenGetTheUserGroups_thenExpectedDataShouldBePresent() {
    List<UserGroup> results = userGroupJpaSpecificationRepository.findAllWithNameInAnyList(
      List.of("Group 1", "Group 2"), List.of("Group 4", "Group 5"));

    assertEquals(2, results.size());
    assertEquals("Group 1", results.get(0).getName());
    assertEquals("Group 4", results.get(1).getName());
}

We can see that the result list contains exactly the expected groups.

我们可以看到,结果列表中包含的正是预期的组别。

7.2. Querydsl

7.2 querydsl

The same functionality we can achieve using Querydsl Predicate. Let’s create another Spring Data JPA repository for the same entity:

我们可以使用 Querydsl Predicate 实现相同的功能。让我们为同一实体创建另一个 Spring Data JPA 存储库:

public interface UserGroupQuerydslPredicateRepository 
  extends JpaRepository<UserGroup, Long>, QuerydslPredicateExecutor<UserGroup> {

    default List<UserGroup> findAllWithNameInAnyList(List<String> names1, List<String> names2) {
        return StreamSupport
          .stream(findAll(predicateInAnyList(names1, names2)).spliterator(), false)
          .collect(Collectors.toList());
    }

    default Predicate predicateInAnyList(List<String> names1, List<String> names2) {
        return new BooleanBuilder().and(QUserGroup.userGroup.name.in(names1))
          .or(QUserGroup.userGroup.name.in(names2));
    }
}

QuerydslPredicateExecutor provides only Iterable as the container for multiple results. If we want to use another type, we must handle the conversions ourselves. As we can see, the client code for this repository will be very similar to that for JPA specifications:

QuerydslPredicateExecutor 仅提供 Iterable 作为多个结果的容器。如果我们想使用其他类型,就必须自己处理转换。正如我们所见,该资源库的客户端代码与 JPA 规范的客户端代码非常相似:

@Test
void givenQuerydslPredicateRepository_whenGetTheUserGroups_thenExpectedDataShouldBePresent() {
    List<UserGroup> results = userQuerydslPredicateRepository.findAllWithNameInAnyList(
      List.of("Group 1", "Group 2"), List.of("Group 4", "Group 5"));

    assertEquals(2, results.size());
    assertEquals("Group 1", results.get(0).getName());
    assertEquals("Group 4", results.get(1).getName());
}

8. Performance

8.性能

Querydsl ultimately prepares the same criteria query but introduces additional conventions beforehand. Let’s measure how this process impacts the performance of our queries. To measure execution time, we can use our IDE functionality or create a timing extension.

Querydsl 最终准备了相同的标准查询,但事先引入了额外的约定。让我们来测量一下这一过程对查询性能的影响。要测量执行时间,我们可以使用IDE 功能或创建计时扩展

We’ve executed all our test methods a few times and saved the median result into a list:

我们已多次执行所有测试方法,并将中位结果保存到列表中:

Method [givenJpaSpecificationRepository_whenGetTheUserGroups_thenExpectedDataShouldBePresent] took 128 ms.
Method [givenQuerydslPredicateRepository_whenGetTheUserGroups_thenExpectedDataShouldBePresent] took 27 ms.
Method [givenJpaCriteria_whenGetAllTheUserGroups_thenExpectedNumberOfItemsShouldBePresent] took 1 ms.
Method [givenQueryDSL_whenGetAllTheUserGroups_thenExpectedNumberOfItemsShouldBePresent] took 3 ms.
Method [givenJpaCriteria_whenModifyTheUserGroup_thenNameShouldBeUpdated] took 13 ms.
Method [givenQueryDSL_whenModifyTheUserGroup_thenNameShouldBeUpdated] took 161 ms.
Method [givenJpaCriteria_whenGetTheUserGroupsWithJoins_thenExpectedDataShouldBePresent] took 887 ms.
Method [givenQueryDSL_whenGetTheUserGroupsWithJoins_thenExpectedDataShouldBePresent] took 728 ms.
Method [givenJpaCriteria_whenGetTheUserGroups_thenExpectedAggregatedDataShouldBePresent] took 5 ms.
Method [givenQueryDSL_whenGetTheUserGroups_thenExpectedAggregatedDataShouldBePresent] took 88 ms.

As we can see, in most cases, we achieve similar execution times for both Querydsl and JPA Criteria. In the modification case, Querydsl uses JPQLSerializer and prepares a JPQL query string, which leads to additional overhead.

我们可以看到,在大多数情况下,Querydsl 和 JPA Criteria 的执行时间相似。在修改案例中,Querydsl 使用 JPQLSerializer 并准备 JPQL 查询字符串,这导致了额外的开销。

9. Conclusion

9.结论

In this article, we’ve thoroughly compared JPA Criteria and Querydsl in various scenarios. In many cases, Querydsl emerged as the preferable option due to its slightly more user-friendly syntax. If a few more dependencies in the project are not an issue, we can consider it a good tool to improve our code readability. On the other hand, we can achieve all the functionality using JPA criteria.

在本文中,我们全面比较了 JPA Criteria 和 Querydsl 在不同场景下的表现。在许多情况下,由于 Querydsl 的语法稍显友好,因此它成为了更好的选择。如果项目中多几个依赖关系不是问题,我们可以将其视为提高代码可读性的好工具。另一方面,我们可以使用 JPA 标准实现所有功能。

As usual, the full source code can be found over on GitHub.

像往常一样,完整的源代码可以在 GitHub 上找到