Blaze Persistence入门

2025/04/14

1. 简介

在本教程中,我们将讨论在Spring Boot应用程序中使用Blaze Persistence库。

该库提供了丰富的Criteria API,用于以编程方式创建SQL查询,它允许我们应用各种过滤器、函数和逻辑条件。

我们将介绍项目设置,提供一些如何创建查询的示例以及如何将实体映射到DTO对象。

2. Maven依赖

为了在我们的项目中包含Blaze Persistence核心,我们需要在pom.xml文件中添加依赖blaze-persistence-core-api-jakartablaze-persistence-core-impl-jakartablaze-persistence-integration-hibernate-6.2

<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-core-api-jakarta</artifactId>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-core-impl-jakarta</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-integration-hibernate-6.2</artifactId>
    <scope>runtime</scope>
</dependency>

根据我们使用的Hibernate版本,后者的依赖可能会有所不同。

3. 实体模型

首先,我们来定义一下示例中使用的数据模型。为了自动创建表,我们将使用Hibernate。

我们有两个实体,Person和Post,它们使用一对多关系连接:

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private int age;

    @OneToMany(mappedBy = "author")
    private Set<Post> posts = new HashSet<>();
}
@Entity
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    private String content;

    @ManyToOne(fetch = FetchType.LAZY)
    private Person author;
}

4. Criteria API

Blaze Persistence库是JPA Criteria API的替代方案,这两个API都使我们能够在运行时定义动态查询。

然而,JPA Criteria API在开发人员中并不那么受欢迎,因为它难以读写。相比之下,Blaze Persistence的设计更加用户友好,更易于使用。此外,它与各种JPA实现集成,并提供了丰富的查询功能。

4.1 配置

为了使用Blaze Persistence Criteria API,我们需要在配置类中定义CriteriaBuilderFactory Bean

@Autowired
private EntityManagerFactory entityManagerFactory;

@Bean
public CriteriaBuilderFactory createCriteriaBuilderFactory() {
    CriteriaBuilderConfiguration config = Criteria.getDefault();
    return config.createCriteriaBuilderFactory(entityManagerFactory);
}

4.2 基本查询

现在,让我们从一个简单的查询开始,该查询从数据库中选择每个Post,我们只需要两个方法调用来定义和执行查询:

List<Post> posts = builderFactory.create(entityManager, Post.class).getResultList();

create方法创建一个查询,而getResultList方法的调用返回查询返回的结果。

此外,在create方法中,Post.class参数有几个用途:

  • 标识查询的结果类型
  • 标识隐式查询根
  • 为Post表添加隐式SELECT和FROM子句

一旦执行,查询将生成以下JPQL:

SELECT post
FROM Post post;

4.3 Where子句

我们可以在create方法之后调用where方法来在我们的标准构建器中添加WHERE子句。

让我们看看如何获取由至少发表过两篇帖子并且年龄在18至40岁之间的人所发表的帖子:

CriteriaBuilder<Person> personCriteriaBuilder = builderFactory.create(entityManager, Person.class, "p")
    .where("p.age")
        .betweenExpression("18")
        .andExpression("40")
    .where("SIZE(p.posts)").geExpression("2")
    .orderByAsc("p.name")
    .orderByAsc("p.id");

由于Blaze Persistence支持直接函数调用语法,我们可以轻松检索与该人相关的帖子的大小

此外,我们可以通过调用whereAnd或whereOr方法来定义复合谓词。它们返回构建器实例,我们可以使用该实例通过一次或多次调用where方法来定义嵌套的复合谓词。完成后,我们需要调用endAnd或endOr方法来关闭复合谓词。

例如,让我们创建一个查询来选择具有特定标题或作者姓名的帖子:

CriteriaBuilder<Post> postCriteriaBuilder = builderFactory.create(entityManager, Post.class, "p")
    .whereOr()
        .where("p.title").like().value(title + "%").noEscape()
        .where("p.author.name").eq(authorName)
    .endOr();

4.4 From子句

FROM子句包含需要查询的实体,如前所述,我们可以在create方法中指定根实体。但是,我们可以定义from子句来指定根实体。这样,隐式创建的查询根将被删除:

CriteriaBuilder<Post> postCriteriaBuilder = builderFactory.create(entityManager, Post.class)
    .from(Person.class, "person")
    .select("person.posts");

在这个例子中,Post.class参数只定义返回类型。

由于我们从不同的表中进行选择,因此构建器将在生成的查询中添加隐式JOIN:

SELECT posts_1 
FROM Person person 
LEFT JOIN person.posts posts_1;

5. 实体视图模块

Blaze Persistence实体视图模块试图解决实体类和DTO类之间的高效映射问题,使用此模块,我们可以将DTO类定义为接口,并使用注解提供到实体类的映射。

5.1 Maven依赖

我们需要在项目中包含额外的实体视图依赖

<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-entity-view-api-jakarta</artifactId>
</dependency>
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-entity-view-impl-jakarta</artifactId>
</dependency>
<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-entity-view-processor-jakarta</artifactId>
</dependency>

5.2 配置

此外,我们需要一个注册了实体视图类的EntityViewManager Bean:

@Bean
public EntityViewManager createEntityViewManager(CriteriaBuilderFactory criteriaBuilderFactory, EntityViewConfiguration entityViewConfiguration) {
    return entityViewConfiguration.createEntityViewManager(criteriaBuilderFactory);
}

因为EntityViewManager同时绑定到EntityManagerFactory和CriteriaBuilderFactory,所以它的范围应该相同

5.3 映射

实体视图表示是一个简单的接口或一个抽象类,描述我们想要的投影的结构。

让我们创建一个接口来代表Post类的实体视图:

@EntityView(Post.class)
public interface PostView {
  
    @IdMapping
    Long getId();

    String getTitle();

    String getContent();
}

我们需要用@EntityView注解来标注我们的接口并提供一个实体类

虽然并非强制要求,但我们应该尽可能使用@IdMapping注解定义id映射,没有此类映射的实体视图会使用equals和hashCode实现来考虑所有属性,而使用id映射时,只会考虑id。

但是,如果我们想为Getter方法使用不同的名称,可以添加@Mapping注解。使用此注解,我们也可以定义整个表达式:

@Mapping("UPPER(title)")
String getTitle();

因此,映射将返回Post实体的大写标题。

此外,我们可以扩展实体视图。假设我们想要定义一个视图,它返回一篇包含附加作者信息的文章。

首先,我们定义一个PersonView接口:

@EntityView(Person.class)
public interface PersonView {
    
    @IdMapping
    Long getId();

    int getAge();

    String getName();
}

其次,我们定义一个扩展PostView接口的新接口和一个返回PersonView信息的方法:

@EntityView(Post.class)
public interface PostWithAuthorView extends PostView {
    PersonView getAuthor();
}

最后,让我们将视图与条件构建器一起使用,它们可以直接应用于查询。

我们可以定义一个基本查询,然后创建映射:

CriteriaBuilder<Post> postCriteriaBuilder = builderFactory.create(entityManager, Post.class, "p")
    .whereOr()
        .where("p.title").like().value("title%").noEscape()
        .where("p.author.name").eq(authorName)
    .endOr();

CriteriaBuilder<PostWithAuthorView> postWithAuthorViewCriteriaBuilder =
    viewManager.applySetting(EntityViewSetting.create(PostWithAuthorView.class), postCriteriaBuilder);

上面的代码将创建一个优化的查询并根据结果构建我们的实体视图:

SELECT p.id AS PostWithAuthorView_id,
       p.author.id AS PostWithAuthorView_author_id,
       author_1.age AS PostWithAuthorView_author_age,
       author_1.name AS PostWithAuthorView_author_name,
       p.content AS PostWithAuthorView_content,
       UPPER(p.title) AS PostWithAuthorView_title
FROM cn.tuyucheng.taketoday.model.Post p
         LEFT JOIN p.author author_1
WHERE p.title LIKE REPLACE(:param_0, '\\', '\\\\')
   OR author_1.name = :param_1

5.4 实体视图和Spring Data

除了与Spring集成之外,Blaze Persistence还提供了Spring Data集成模块,使实体视图的使用就像使用实体一样方便。

此外,我们还需要包含Spring集成依赖

<dependency>
    <groupId>com.blazebit</groupId>
    <artifactId>blaze-persistence-integration-spring-data-3.1</artifactId>
</dependency>

此外,为了启用Spring Data,我们需要在配置类上添加@EnableBlazeRepositories注解。此外,我们还可以指定Repository类扫描的基础包。

该集成带有一个基本EntityViewRepository接口,我们可以使用它来定义我们的Repository

现在,让我们定义一个与PostWithAuthorView一起使用的接口:

@Repository
@Transactional(readOnly = true)
public interface PostViewRepository extends EntityViewRepository<PostWithAuthorView, Long> {
}

在这里,我们的接口继承了最常用的Repository方法,例如findAll,findOne和exist。如果需要,我们可以使用Spring Data JPA方法命名约定定义自己的方法。

6. 总结

在本文中,我们学习了如何使用Blaze Persistence库配置和创建简单查询。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章