1. Overview
1.概述
Apache DeltaSpike is a project which provides a collection of CDI extensions for Java projects; it requires a CDI implementation to be available at runtime.
Apache DeltaSpike是一个为Java项目提供CDI扩展的项目;它要求在运行时有一个CDI实现。
Of course, it can work with the different implementation of CDI – JBoss Weld or OpenWebBeans. It’s also tested on many application servers.
当然,它可以与CDI的不同实现一起工作 – JBoss Weld或OpenWebBeans。它也在许多应用服务器上进行了测试。
In this tutorial, we’ll focus on one of the best known and useful – Data module.
在本教程中,我们将重点讨论最著名和最有用的模块之一 – 数据模块。
2. DeltaSpike Data Module Setup
2.DeltaSpike数据模块设置
Apache DeltaSpike Data module is used to simplify implementation of the repository pattern. It allows reducing a boilerplate code by providing centralized logic for queries creation and execution.
Apache DeltaSpike数据模块是用来简化资源库模式的实施。它允许通过提供集中的查询创建和执行逻辑来减少模板代码。
It’s very similar to the Spring Data project. To query a database, we need to define a method declaration (without implementation) which follows defined naming convention or which contains @Query annotation. The implementation will be done for us by the CDI extension.
它与Spring Data项目非常相似。为了查询数据库,我们需要定义一个方法声明(没有实现),它遵循定义的命名惯例或包含@Query注释。实现将由CDI扩展为我们完成。
In the next subsections, we’ll cover how to setup Apache DeltaSpike Data module in our application.
在接下来的几个小节中,我们将介绍如何在我们的应用程序中设置Apache DeltaSpike数据模块。
2.1. Required Dependencies
2.1.必要的依赖性
To use Apache DeltaSpike Data module in the application, we need to setup required dependencies.
为了在应用程序中使用Apache DeltaSpike数据模块,我们需要设置必要的依赖关系。
When Maven is our build tool we have to use:
当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>
When we’re using Gradle:
当我们在使用Gradle时。
runtime 'org.apache.deltaspike.modules:deltaspike-data-module-impl'
compile 'org.apache.deltaspike.modules:deltaspike-data-module-api'
Apache DeltaSpike Data module artifacts are available over on Maven Central:
Apache DeltaSpike数据模块的工件可以在Maven中心找到。
To run an application with Data module, we also need a JPA and CDI implementations available at runtime.
为了运行一个带有数据模块的应用程序,我们还需要在运行时提供JPA和CDI实现。
Although it’s possible to run Apache DeltaSpike in Java SE application, in most cases, it will be deployed on the Application Server (e.g., Wildfly or WebSphere).
虽然可以在Java SE应用程序中运行Apache DeltaSpike,但在大多数情况下,它将被部署在应用服务器上(如Wildfly或WebSphere)。
Application Servers have full Jakarta EE support, so we don’t have to do anything more. In case of Java SE application, we have to provide these implementations (e.g., by adding dependencies to the Hibernate and JBoss Weld).
应用服务器有完整的Jakarta EE支持,所以我们不需要再做什么。如果是Java SE应用程序,我们必须提供这些实现(例如,通过向Hibernate和JBoss Weld添加依赖项)。
Next, we’ll also cover required configuration for EntityManager.
接下来,我们还将介绍EntityManager的必要配置。
2.2. Entity Manager Configuration
2.2.实体管理器配置
The Data module requires EntityManager to be injected over CDI.
Data模块需要通过CDI注入EntityManager。
We can achieve this by using a CDI producer:
我们可以通过使用CDI生产商来实现这一目标。
public class EntityManagerProducer {
@PersistenceContext(unitName = "primary")
private EntityManager entityManager;
@ApplicationScoped
@Produces
public EntityManager getEntityManager() {
return entityManager;
}
}
The above code assumes that we have persistence unit with name primary defined in the persistence.xml file.
上述代码假设我们在persistence.xml文件中定义了名称为primary的持久化单元。
Let’s see below as an example of definition:
让我们看看下面这个定义的例子。
<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>
The persistence unit in our example uses JTA transaction type which means we have to provide a Transaction Strategy we’re going to use.
我们例子中的持久化单元使用JTA事务类型,这意味着我们必须提供一个我们要使用的事务策略。
2.3. Transaction Strategy
2.3.事务策略
In case we’re using JTA transaction type for our data source then we have to define Transaction Strategy that will be used in the Apache DeltaSpike repositories. We can do it inside apache-deltaspike.properties file (under META-INF directory):
如果我们的数据源使用的是JTA事务类型,那么我们必须定义将在Apache DeltaSpike存储库中使用的事务策略。我们可以在apache-deltaspike.properties文件(在META-INF目录下)中完成。
globalAlternatives.org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy=org.apache.deltaspike.jpa.impl.transaction.ContainerManagedTransactionStrategy
There are four types of transaction strategy we can define:
我们可以定义四种类型的交易策略。
- BeanManagedUserTransactionStrategy
- ResourceLocalTransactionStrategy
- ContainerManagedTransactionStrategy
- EnvironmentAwareTransactionStrategy
All of them implement org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy.
所有这些都实现了org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy。
This was the last part of the configuration required for our data module.
这是我们的数据模块所需配置的最后一部分。
Next, we’ll show how to implement the repository pattern classes.
接下来,我们将展示如何实现资源库模式类。
3. Repository Classes
3.存储库类
When we’re using Apache DeltaSpike data module any abstract class or interface can become a repository class.
当我们使用Apache DeltaSpike数据模块时,任何抽象类或接口都可以成为一个存储库类。
All we have to do is to add an @Repository annotation with a forEntity attribute which defines JPA entity that our repository should handle:
我们要做的就是 添加一个@Repository annotation ,其中有一个forEntity属性,定义了我们存储库应该处理的JPA实体。
@Entity
public class User {
// ...
}
@Repository(forEntity = User.class)
public interface SimpleUserRepository {
// ...
}
or with an abstract class:
或者用一个抽象类。
@Repository(forEntity = User.class)
public abstract class SimpleUserRepository {
// ...
}
Data module discovers classes (or interfaces) with such an annotation and it’ll process methods which are inside.
数据模块会发现带有这种注解的类(或接口),它将处理其中的方法。
There are few possibilities to define the query to execute. We’ll cover one by one shortly in the following sections.
有几种可能性来定义要执行的查询。我们将在以下章节中逐一介绍。
4. Query From Method Name
4.从方法名称查询
The first possibility to define a query is to use method name which follows a defined naming convention.
定义查询的第一种可能性是使用遵循定义的命名惯例的方法名称。
It looks like below:
它看起来像下面这样。
(Entity|Optional<Entity>|List<Entity>|Stream<Entity>) (prefix)(Property[Comparator]){Operator Property [Comparator]}
Next, we’ll focus on each part of this definition.
接下来,我们将重点讨论这个定义的每一部分。
4.1. Return Type
4.1.返回类型
The return type mainly defines how many objects our query might return. We cannot define single entity type as a return value in case our query might return more than one result.
返回类型主要定义了我们的查询可能返回多少个对象。我们不能定义单一的实体类型作为返回值,以防我们的查询可能返回多个结果。
The following method will throw an exception in case there is more than one User with given name:
如果有一个以上的User的名字,以下方法将抛出一个异常。
public abstract User findByFirstName(String firstName);
The opposite isn’t true – we can define a return value as a Collection even though the result will be just a single entity.
反之亦然–我们可以将一个返回值定义为Collection,即使结果只是一个单一的实体。
public abstract Collection<User> findAnyByFirstName(String firstName);
The method name prefix which suggests one value as a return type (e.g., findAny) is suppressed in case we define return value as Collection.
如果我们将返回值定义为Collection,那么方法名称的前缀就会被抑制,因为它暗示一个值作为返回类型(例如,findAny)。
The above query will return all Users with a first name matching even the method name prefix suggests something different.
上述查询将返回所有名字匹配的Users,即使方法名称的前缀表明了不同的内容。
Such combinations (Collection return type and a prefix which suggests one single value return) should be avoided because the code becomes not intuitive and hard to understand.
应该避免这样的组合(Collection返回类型和暗示一个单一值返回的前缀),因为代码变得不直观和难以理解。
The next section shows more details about method name prefix.
下一节显示了关于方法名称前缀的更多细节。
4.2. Prefix for Query Method
4.2.查询方法的前缀
Prefix defines the action we want to execute on the repository. The most useful one is to find entities which match given search criteria.
前缀定义了我们要在版本库上执行的动作。最有用的是找到符合给定搜索条件的实体。
There are many prefixes for this action like findBy, findAny, findAll. For the detailed list, please check official Apache DeltaSpike documentation:
这个动作有很多前缀,比如findBy、findAny、findAll。关于详细的列表,请查看Apache DeltaSpike官方文档。
public abstract User findAnyByLastName(String lastName);
However, there are also other method templates which are used for counting and removing entities. We can count all rows in a table:
然而,也有其他方法模板,用于计数和删除实体。我们可以计算一个表中的所有行。
public abstract int count();
Also, remove method template exists which we can add in our repository:
另外,remove方法模板也存在,我们可以在我们的资源库中添加。
public abstract void remove(User user);
Support for countBy and removeBy method prefixes will be added in the next version of Apache DeltaSpike 1.9.0.
对countBy和removeBy方法前缀的支持将在Apache DeltaSpike 1.9.0的下一个版本中加入。
The next section shows how we can add more attributes to the queries.
下一节展示了我们如何向查询添加更多的属性。
4.3. Query With Many Properties
4.3.有许多属性的查询
In the query, we can use many properties combined with and operators.
在查询中,我们可以使用许多属性与and运算符相结合。
public abstract Collection<User> findByFirstNameAndLastName(
String firstName, String lastName);
public abstract Collection<User> findByFirstNameOrLastName(
String firstName, String lastName);
We can combine as many properties as we want. Search for nested properties is also available which we’ll show next.
我们可以根据自己的需要组合任意多的属性。对嵌套属性的搜索也是可用的,我们接下来会展示。
4.4. Query With Nested Properties
4.4.带有嵌套属性的查询
The query can also use nested properties.
查询也可以使用嵌套属性。
In the following example User entity has an address property of type Address and Address entity has a city property:
在下面的例子中,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);
4.5. Order in the Query
4.5.查询中的顺序
DeltaSpike allows us to define an order in which result should be returned. We can define both – ascending and descending order:
DeltaSpike允许我们定义返回结果的顺序。我们可以同时定义升序和降序。
public abstract List<User> findAllOrderByFirstNameAsc();
As shown above all we have to do is to add a part to the method name which contains property name we want to sort by and the short name for the order direction.
如上图所示,我们所要做的就是在方法名称中添加一个部分,其中包含我们想要排序的属性名称和排序方向的简短名称。
We can combine many orders easily:
我们可以很容易地合并许多订单。
public abstract List<User> findAllOrderByFirstNameAscLastNameDesc();
Next, we’ll show how to limit the query result size.
接下来,我们将展示如何限制查询结果的大小。
4.6. Limit Query Result Size and Pagination
4.6.限制查询结果的大小和分页
There are use cases when we want to retrieve few first rows from the whole result. It’s so-called query limit. It’s also straightforward with Data module:
有些情况下,我们希望从整个结果中检索出少数第一行。这就是所谓的查询限制。这在数据模块中也是很简单的。
public abstract Collection<User> findTop2OrderByFirstNameAsc();
public abstract Collection<User> findFirst2OrderByFirstNameAsc();
First and top can be used interchangeably.
第一和顶部可以互换使用。
We can then enable query pagination by providing two additional parameters: @FirstResult and @MaxResult:
然后我们可以通过提供两个额外的参数来启用查询分页功能。@FirstResult和@MaxResult。
public abstract Collection<User> findAllOrderByFirstNameAsc(@FirstResult int start, @MaxResults int size);
We defined already a lot of methods in the repository. Some of them are generic and should be defined once and use by each repository.
我们已经在版本库中定义了很多方法。其中一些是通用的,应该被定义一次,供每个版本库使用。
Apache DeltaSpike provides few basic types which we can use to have a lot of methods out of the box.
Apache DeltaSpike提供了一些基本的类型,我们可以利用这些类型来拥有很多开箱即用的方法。
In the next section, we’ll focus on how to do this.
在下一节,我们将重点讨论如何做到这一点。
5. Basic Repository Types
5.基本的存储库类型
To get some basic repository methods, our repository should extend basic type provided by Apache DeltaSpike. There are some of them like EntityRepository, FullEntityRepository, etc.:
为了获得一些基本的版本库方法,我们的版本库应该扩展Apache DeltaSpike提供的基本类型。其中有一些像EntityRepository,FullEntityRepository,等。
@Repository
public interface UserRepository
extends FullEntityRepository<User, Long> {
// ...
}
Or using an abstract class:
或者使用一个抽象的类。
@Repository
public abstract class UserRepository extends AbstractEntityRepository<User, Long> {
// ...
}
The above implementation gives us a lot of methods without writing additional lines of code, so we gained what we wanted – we reduce boilerplate code massively.
上面的实现给了我们很多方法,而没有写额外的代码行,所以我们获得了我们想要的东西–我们大量减少了模板代码。
In case we’re using base repository type there’s no need to pass an additional forEntity attribute value to our @Repository annotation.
如果我们使用基础仓库类型,就不需要向我们的@Repository注解传递一个额外的forEntity属性值。
When we’re using abstract classes instead of interfaces for our repositories we get an additional possibility to create a custom query.
当我们使用抽象类而不是接口来建立我们的资源库时,我们得到了一个额外的可能性来创建一个自定义查询。
Abstract base repository classes, e.g., AbstractEntityRepository gives us an access to fields (via getters) or utility methods which we can use to create a query:
抽象的基础存储库类,例如,AbstractEntityRepository给我们一个访问字段(通过getters)或实用方法的机会,我们可以用它来创建一个查询。
public List<User> findByFirstName(String firstName) {
return typedQuery("select u from User u where u.firstName = ?1")
.setParameter(1, firstName)
.getResultList();
}
In the above example, we used a typedQuery utility method to create a custom implementation.
在上面的例子中,我们使用了一个typedQuery实用方法来创建一个自定义实现。
The last possibility to create a query is to use @Query annotation which we will show next.
创建查询的最后一种可能性是使用@Query注解,我们接下来会展示。
6. @Query Annotation
6.@Query 注释
The SQL query to execute can also be defined with the @Query annotation. It’s very similar to the Spring solution. We have to add an annotation to the method with SQL query as a value.
要执行的SQL查询也可以用@Query注解来定义。这与Spring的解决方案非常相似。我们必须在方法中添加一个注解,将SQL查询作为一个值。
By default this is a JPQL query:
默认情况下,这是一个JPQL查询。
@Query("select u from User u where u.firstName = ?1")
public abstract Collection<User> findUsersWithFirstName(String firstName);
As in the above example, we can easily pass parameters to the query via an index.
如同上面的例子,我们可以很容易地通过索引向查询传递参数。
In case we want to pass query via native SQL instead of JPQL we need to define additional query attribute – isNative with true value:
如果我们想通过本地SQL而不是JPQL来传递查询,我们需要定义额外的查询属性–isNative,值为true。
@Query(value = "select * from User where firstName = ?1", isNative = true)
public abstract Collection<User> findUsersWithFirstNameNative(String firstName);
7. Conclusion
7.结论
In this article, we covered the basic definition of Apache DeltaSpike, and we focused on the exciting part – Data module. It’s very similar to the Spring Data Project.
在这篇文章中,我们介绍了Apache DeltaSpike的基本定义,并重点介绍了令人兴奋的部分–数据模块。它与Spring Data项目非常相似。
We explored how to implement the repository pattern. We also introduced three possibilities how to define a query to execute.
我们探讨了如何实现资源库模式。我们还介绍了如何定义执行查询的三种可能性。
As always, the complete code examples used in this article are available over on Github.
像往常一样,本文中使用的完整代码示例可在Github上获得。