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);
⚠️ 注意:countBy
和 removeBy
前缀的支持计划在 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
关联 Address
,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);
✅ 生成的 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
);
调用时传入分页参数即可,框架自动处理 LIMIT
或 ROWNUM
。
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