1. 概述

Apache DeltaSpike 是一个为 Java 项目提供 CDI 扩展集合 的开源项目,它依赖运行时存在 CDI 实现。

✅ 它兼容主流 CDI 实现,比如 JBoss Weld 和 OpenWebBeans,并且在 WildFly、WebSphere 等多种应用服务器上经过充分测试。

本文重点介绍其中最受欢迎、最实用的模块之一:Data 模块

这个模块的目标很明确:简化 Repository 模式的实现,通过集中管理查询的创建与执行,大幅减少模板代码。熟悉 Spring Data 的同学会发现,它的设计思路非常相似 —— 只需声明方法,无需写实现,框架自动生成查询逻辑。


2. DeltaSpike Data 模块的配置

2.1 必要依赖

要使用 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 配置

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

这些依赖可在 Maven Central 找到:

⚠️ 注意:Data 模块本身不包含 JPA 或 CDI 实现,因此你的项目必须确保运行时环境中存在这两者。

  • 应用服务器场景(如 WildFly、WebSphere):通常自带完整的 Jakarta EE 支持,无需额外配置。
  • Java SE 环境:需要手动引入 JPA(如 Hibernate)和 CDI(如 Weld SE)实现。

接下来我们配置 EntityManager

2.2 EntityManager 配置

Data 模块要求通过 CDI 注入 EntityManager,最标准的做法是使用 CDI Producer 方法

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/baeldung-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 事务策略(Transaction Strategy)

如果你的数据源使用 JTA 事务,必须在 DeltaSpike 中显式指定事务策略。方式是在 META-INF/apache-deltaspike.properties 文件中添加配置:

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

DeltaSpike 提供了四种事务策略实现:

  • BeanManagedUserTransactionStrategy:Bean 管理事务
  • ResourceLocalTransactionStrategy:本地资源事务
  • ContainerManagedTransactionStrategy:容器管理事务(适用于 JTA)
  • EnvironmentAwareTransactionStrategy:环境感知型策略,自动选择

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

至此,Data 模块的基础配置已完成,可以开始写 Repository 了。


3. Repository 类的定义

在 DeltaSpike 中,任何接口或抽象类都可以成为 Repository,只需加上 @Repository 注解,并通过 forEntity 指定对应的 JPA 实体。

接口形式

@Entity
public class User {
    // ...
}  

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

抽象类形式

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

✅ DeltaSpike 会自动扫描带有 @Repository 注解的类,并处理其中的方法,根据命名或注解生成对应的查询实现。


4. 通过方法名定义查询

这是最“约定优于配置”的方式,只需按命名规范写方法名,框架自动生成 JPQL 查询。

语法规则如下:

(返回类型) (前缀)(属性[比较符]){操作符 属性 [比较符]}

下面我们逐个拆解。

4.1 返回类型

返回类型决定了查询结果的预期数量。如果方法返回单个实体,但实际结果多于一条,会抛出异常。

public abstract User findByFirstName(String firstName);

⚠️ 踩坑提示:如果数据库中存在多个同名用户,上面的方法会抛出 NonUniqueResultException

反之,返回集合类型则无此限制:

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

即使方法名用了 findAny(暗示单条结果),但返回类型是集合,框架仍会返回所有匹配项。

❌ 不推荐这种“名不副实”的写法,容易误导团队成员。

4.2 方法名前缀

前缀定义了操作类型,常见的有:

  • findBy / findAny / findAll:查询
  • count:统计数量
  • remove:删除实体

示例:

public abstract User findAnyByLastName(String lastName);
public abstract int count();
public abstract void remove(User user);

⚠️ 注意:countByremoveBy 前缀的支持计划在 DeltaSpike 1.9.0 版本中加入,当前版本暂不支持。

4.3 多属性查询

支持通过 And / Or 组合多个属性条件:

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

属性数量不限,支持任意组合。

4.4 嵌套属性查询

支持通过下划线 _ 访问关联对象的属性。

例如,User 关联 AddressAddresscity 字段:

@Entity
public class Address {
    private String city;
    // ...
}

@Entity
public class User {
    @OneToOne 
    private Address address;
    // ...
}

public abstract Collection<User> findByAddress_city(String city);

✅ 生成的 JPQL 会自动处理 JOIN

4.5 查询结果排序

通过 OrderBy + 属性名 + 方向(Asc/Desc)实现排序:

public abstract List<User> findAllOrderByFirstNameAsc();

支持多字段排序:

public abstract List<User> findAllOrderByFirstNameAscLastNameDesc();

4.6 限制结果数量与分页

限制返回条数

public abstract Collection<User> findTop2OrderByFirstNameAsc();
public abstract Collection<User> findFirst2OrderByFirstNameAsc(); // top 和 first 可互换

分页支持

通过 @FirstResult@MaxResults 参数实现分页:

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

调用时传入分页参数即可,框架自动处理 LIMITROWNUM


5. 基础 Repository 类型

DeltaSpike 提供了几种内置的基类,让你免费获得一堆常用方法,进一步减少样板代码。

接口继承

@Repository
public interface UserRepository extends FullEntityRepository<User, Long> {
    // 自动拥有 save, delete, findById, findAll 等方法
}

抽象类继承

@Repository
public abstract class UserRepository extends AbstractEntityRepository<User, Long> {
    // 同样拥有基础 CRUD 方法
}

✅ 优势:

  • 无需在 @Repository 中重复写 forEntity,泛型已明确实体类型。
  • 抽象类形式允许你编写自定义查询逻辑。

自定义查询示例

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

typedQuery()AbstractEntityRepository 提供的便捷方法,可直接用于构建 JPQL。


6. 使用 @Query 注解

当方法名无法满足复杂查询需求时,可使用 @Query 注解直接写 JPQL 或原生 SQL。

JPQL 查询

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

参数通过 ?1, ?2 占位符传递。

原生 SQL 查询

通过 isNative = true 指定使用原生 SQL:

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

⚠️ 注意:原生 SQL 返回的是 Object[] 或 Map,若要映射为实体,需确保字段名匹配或使用 @SqlResultSetMapping


7. 总结

本文系统介绍了 Apache DeltaSpike Data 模块的核心用法:

  • ✅ 配置 CDI、JPA 和事务策略
  • ✅ 定义 Repository 接口或抽象类
  • ✅ 三种查询方式:方法名推导、@Query 注解、自定义实现
  • ✅ 内置基础 Repository 类大幅减少模板代码

💡 适用场景:
适合使用 CDI 的 Jakarta EE 项目,尤其是想摆脱繁琐 DAO 层、追求简洁 Repository 模式的团队。其设计理念与 Spring Data 高度相似,学习成本低。

完整示例代码已托管至 GitHub:

https://github.com/baeldung/tutorials/tree/master/persistence-modules/deltaspike


原始标题:A Guide to DeltaSpike Data Module