Contents

Apache DeltaSpike 简介

1. 概述

Apache DeltaSpike 是一个为 Java 项目提供**CDI 扩展集合的项目;**它需要 CDI 实现在运行时可用。

当然,它可以与 CDI 的不同实现一起使用——JBoss Weld 或 OpenWebBeans。它还在许多应用服务器上进行了测试。

在本教程中,我们将专注于最著名和最有用的模块之一——数据模块

2. DeltaSpike 数据模块设置

Apache DeltaSpike Data 模块用于简化存储库模式的实现。它允许通过为查询创建和执行提供集中逻辑来减少样板代码

它与Spring Data 项目非常相似。要查询数据库,我们需要定义一个方法声明(没有实现),它遵循定义的命名约定或包含*@Query*注解。实施将由 CDI 扩展为我们完成。

在接下来的小节中,我们将介绍如何在我们的应用程序中设置 Apache DeltaSpike Data 模块。

2.1. 所需的依赖项

要在应用程序中使用 Apache DeltaSpike Data 模块,我们需要设置所需的依赖项。

当 Maven 是我们的构建工具时,我们必须使用:

<dependency>
    <groupId>org.apache.deltaspike.modules</groupId>
    <artifactId>deltaspike-data-module-api</artifactId>
    <version>1.8.2</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.apache.deltaspike.modules</groupId>
    <artifactId>deltaspike-data-module-impl</artifactId>
    <version>1.8.2</version>
    <scope>runtime</scope>
</dependency>

当我们使用 Gradle 时:

runtime 'org.apache.deltaspike.modules:deltaspike-data-module-impl'
compile 'org.apache.deltaspike.modules:deltaspike-data-module-api'

Apache DeltaSpike Data 模块工件在 Maven Central 上可用:

使用 Data 模块运行应用程序,我们还需要在运行时可用的 JPA 和 CDI 实现

尽管可以在 Java SE 应用程序中运行 Apache DeltaSpike,但在大多数情况下,它将部署在应用程序服务器(例如 Wildfly 或 WebSphere)上。

应用服务器拥有完整的 Jakarta EE 支持,因此我们无需再做任何事情。对于 Java SE 应用程序,我们必须提供这些实现(例如,通过向 Hibernate 和 JBoss Weld 添加依赖项)。

接下来,我们还将介绍EntityManager所需的配置。

2.2. 实体管理器配置

Data 模块需要通过CDI 注入EntityManager

我们可以通过使用 CDI 生产者来实现这一点:

public class EntityManagerProducer {
    @PersistenceContext(unitName = "primary")
    private EntityManager entityManager;
    @ApplicationScoped
    @Produces
    public EntityManager getEntityManager() {
        return entityManager;
    }
}

上面的代码假设我们在persistence.xml文件中定义了名为primary的持久性单元。

让我们看下面的定义示例:

<persistence-unit name="primary" transaction-type="JTA">
   <jta-data-source>java:jboss/datasources/blogdemo-jee7-seedDS</jta-data-source>
   <properties>
      <property name="hibernate.hbm2ddl.auto" value="create-drop" />
      <property name="hibernate.show_sql" value="false" />
   </properties>
</persistence-unit>

我们示例中的持久性单元使用 JTA 事务类型,这意味着我们必须提供我们将要使用的事务策略。

2.3. 事务策略

如果我们对数据源使用 JTA 事务类型,那么我们必须定义将在 Apache DeltaSpike 存储库中使用的事务策略。我们可以在apache-deltaspike.properties文件中(在META-INF目录下)执行此操作:

globalAlternatives.org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy=org.apache.deltaspike.jpa.impl.transaction.ContainerManagedTransactionStrategy

我们可以定义四种类型的交易策略:

  • BeanManagedUserTransactionStrategy
  • ResourceLocalTransactionStrategy
  • ContainerManagedTransactionStrategy
  • EnvironmentAwareTransactionStrategy

它们都实现了 org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy

这是我们的数据模块所需配置的最后一部分。

接下来,我们将展示如何实现存储库模式类。

3. 存储库类

当我们使用 Apache DeltaSpike 数据模块时,任何抽象类或接口都可以成为存储库类。

我们所要做的就是添加一个带有forEntity属性的@Repository*注解*,该属性定义了我们的存储库应该处理的 JPA 实体:

@Entity
public class User {
    // ...
}  
@Repository(forEntity = User.class) 
public interface SimpleUserRepository { 
    // ... 
}

或使用抽象类:

@Repository(forEntity = User.class)
public abstract class SimpleUserRepository { 
    // ... 
}

数据模块发现具有此类注解的类(或接口),并将处理内部的方法。

定义要执行的查询的可能性很小。我们将在以下部分中一一介绍。

4. 从方法名查询

定义查询的第一种可能性是使用遵循已定义命名约定的方法名称。 如下所示:

(Entity|Optional<Entity>|List<Entity>|Stream<Entity>) (prefix)(Property[Comparator]){Operator Property [Comparator]}

接下来,我们将关注这个定义的每个部分。

4.1. 返回类型

返回类型主要定义我们的查询可能返回多少对象。我们不能将单个实体类型定义为返回值,以防我们的查询可能返回多个结果。

如果有多个具有给定名称的User,则以下方法将引发异常:

public abstract User findByFirstName(String firstName);

反之则不然——我们可以将返回值定义为Collection,即使结果只是一个实体。

public abstract Collection<User> findAnyByFirstName(String firstName);

如果我们将返回值定义为Collection,建议将一个值作为返回类型(例如findAny )的方法名称前缀将被禁止。

上面的查询将返回所有名字匹配的User,即使方法名称前缀暗示了一些不同的东西。

应该避免这种组合(Collection返回类型和建议单个值返回的前缀),因为代码变得不直观且难以理解。

下一节将展示有关方法名称前缀的更多详细信息。

4.2. 查询方法前缀

前缀定义了我们要在存储库上执行的操作。最有用的一个是找到与给定搜索条件匹配的实体。 这个动作有很多前缀,比如findByfindAnyfindAll。详细列表请查看 Apache DeltaSpike 官方文档

public abstract User findAnyByLastName(String lastName);

但是,还有其他用于计算和删除实体的方法模板。我们可以count表中的所有行:

public abstract int count();

此外,remove方法模板存在,我们可以将其添加到我们的存储库中:

public abstract void remove(User user);

countBy 和removeBy方法前缀的支持将在 Apache DeltaSpike 1.9.0 的下一个版本中添加。

下一节将展示我们如何向查询添加更多属性。

4.3. 具有许多属性的查询

在查询中,我们可以使用and运算符组合的许多属性

public abstract Collection<User> findByFirstNameAndLastName(
  String firstName, String lastName);
public abstract Collection<User> findByFirstNameOrLastName(
  String firstName, String lastName);

我们可以组合任意数量的属性。也可以搜索嵌套属性,我们将在下面展示。

4.4. 使用嵌套属性查询

查询也可以使用嵌套属性。

在以下示例中,User实体具有地址类型的地址属性,Address实体具有city属性:

@Entity
public class Address {
private String city;
    // ...
}
@Entity
public class User {
    @OneToOne 
    private Address address;
    // ...
}
public abstract Collection<User> findByAddress_city(String city);

4.5. 查询中的顺序

DeltaSpike 允许我们定义返回结果的顺序。我们可以同时定义升序和降序:

public abstract List<User> findAllOrderByFirstNameAsc();

如上所示,我们所要做的就是在方法名称中添加一个部分,其中包含我们要排序的属性名称和订单方向的短名称。 我们可以轻松组合多个订单:

public abstract List<User> findAllOrderByFirstNameAscLastNameDesc();

接下来,我们将展示如何限制查询结果的大小。

4.6. 限制查询结果大小和分页

当我们想从整个结果中检索几行第一行时,有些用例。这就是所谓的查询限制。使用 Data 模块也很简单:

public abstract Collection<User> findTop2OrderByFirstNameAsc();
public abstract Collection<User> findFirst2OrderByFirstNameAsc();

Firsttop可以互换使用。

然后,我们可以通过提供两个附加参数来启用查询分页:@FirstResult和*@MaxResult*:

public abstract Collection<User> findAllOrderByFirstNameAsc(@FirstResult int start, @MaxResults int size);

我们已经在存储库中定义了很多方法。其中一些是通用的,应该定义一次并由每个存储库使用。

Apache DeltaSpike 提供了一些基本类型,我们可以使用它们来拥有很多开箱即用的方法。

在下一节中,我们将重点介绍如何做到这一点。

5. 基本存储库类型

为了获得一些基本的存储库方法,我们的存储库应该扩展 Apache DeltaSpike 提供的基本类型。其中有一些,例如EntityRepositoryFullEntityRepository等:

@Repository
public interface UserRepository 
  extends FullEntityRepository<User, Long> {
    // ...
}

或者使用抽象类:

@Repository
public abstract class UserRepository extends AbstractEntityRepository<User, Long> {
    // ...
}

上面的实现为我们提供了很多方法,而无需编写额外的代码行,因此我们得到了我们想要的——我们大量减少了样板代码。

如果我们使用基本存储库类型,则无需将额外的forEntity 属性值传递给我们的*@Repository*注解。

当我们为存储库使用抽象类而不是接口时,我们就有了创建自定义查询的额外可能性。

抽象基础存储库类,例如 AbstractEntityRepository让我们可以访问字段(通过 getter)或实用方法,我们可以使用它们来创建查询

public List<User> findByFirstName(String firstName) {
    return typedQuery("select u from User u where u.firstName = ?1")
      .setParameter(1, firstName)
      .getResultList();
}

在上面的示例中,我们使用了typedQuery 实用程序方法来创建自定义实现。

创建查询的最后一种可能性是使用我们将在下面展示的*@Query*注解。

6. @Query注解

要执行的 SQL查询也可以使用*@Query*注解来定义。它与 Spring 解决方案非常相似。我们必须以 SQL 查询作为值向方法添加注解。

默认情况下,这是一个 JPQL 查询:

@Query("select u from User u where u.firstName = ?1")
public abstract Collection<User> findUsersWithFirstName(String firstName);

如上例所示,我们可以通过索引轻松地将参数传递给查询。

如果我们想通过原生 SQL 而不是 JPQL 传递查询,我们需要定义额外的查询属性 – isNative与真值:

@Query(value = "select * from User where firstName = ?1", isNative = true)
public abstract Collection<User> findUsersWithFirstNameNative(String firstName);