Un guide du module de données DeltaSpike

1. Vue d'ensemble

Apache DeltaSpike est un projet qui fournit une collection d'extensions CDI pour les projets Java; il nécessite une implémentation CDI pour être disponible au moment de l'exécution.

Bien sûr, il peut fonctionner avec les différentes implémentations de CDI - JBoss Weld ou OpenWebBeans. Il est également testé sur de nombreux serveurs d'applications.

Dans ce didacticiel, nous allons nous concentrer sur l' un des modules de données les plus connus et les plus utiles .

2. Configuration du module de données DeltaSpike

Le module Apache DeltaSpike Data est utilisé pour simplifier la mise en œuvre du modèle de référentiel . Il permet de réduire un code standard en fournissant une logique centralisée pour la création et l'exécution des requêtes .

C'est très similaire au projet Spring Data. Pour interroger une base de données, nous devons définir une déclaration de méthode (sans implémentation) qui suit la convention de dénomination définie ou qui contient l' annotation @Query . L'implémentation se fera pour nous par l'extension CDI.

Dans les sous-sections suivantes, nous verrons comment configurer le module Apache DeltaSpike Data dans notre application.

2.1. Dépendances requises

Pour utiliser le module Apache DeltaSpike Data dans l'application, nous devons configurer les dépendances requises.

Lorsque Maven est notre outil de construction, nous devons utiliser:

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

Lorsque nous utilisons Gradle:

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

Les artefacts du module Apache DeltaSpike Data sont disponibles à l'adresse over sur Maven Central:

  • deltaspike-data-module-impl
  • deltaspike-data-module-api

Pour exécuter une application avec le module Data, nous avons également besoin d'une implémentation JPA et CDI disponible au moment de l'exécution .

Bien qu'il soit possible d'exécuter Apache DeltaSpike dans une application Java SE, dans la plupart des cas, il sera déployé sur le serveur d'applications (par exemple, Wildfly ou WebSphere).

Les serveurs d'applications ont une prise en charge complète de Jakarta EE, nous n'avons donc rien à faire de plus. Dans le cas d'une application Java SE, nous devons fournir ces implémentations (par exemple, en ajoutant des dépendances à Hibernate et JBoss Weld).

Ensuite, nous aborderons également la configuration requise pour EntityManager .

2.2. Configuration de Entity Manager

Le module de données nécessite que EntityManager soit injecté via CDI .

Nous pouvons y parvenir en utilisant un producteur CDI:

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

Le code ci-dessus suppose que nous avons une unité de persistance avec le nom primaire défini dans le fichier persistence.xml .

Voyons ci-dessous un exemple de définition:

 java:jboss/datasources/baeldung-jee7-seedDS     

L'unité de persistance dans notre exemple utilise le type de transaction JTA, ce qui signifie que nous devons fournir une stratégie de transaction que nous allons utiliser.

2.3. Stratégie de transaction

Si nous utilisons le type de transaction JTA pour notre source de données, nous devons définir la stratégie de transaction qui sera utilisée dans les référentiels Apache DeltaSpike . Nous pouvons le faire dans le fichier apache-deltaspike.properties (sous le répertoire META-INF ):

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

Il existe quatre types de stratégie de transaction que nous pouvons définir:

  • BeanManagedUserTransactionStrategy
  • ResourceLocalTransactionStrategy
  • ContainerManagedTransactionStrategy
  • EnvironnementAwareTransactionStrategy

Tous implémentent org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy .

C'était la dernière partie de la configuration requise pour notre module de données.

Ensuite, nous montrerons comment implémenter les classes de modèles de référentiel.

3. Classes de référentiel

Lorsque nous utilisons le module de données Apache DeltaSpike, toute classe ou interface abstraite peut devenir une classe de référentiel .

Tout ce que nous avons à faire est d' ajouter une annotation @Repository avec un attribut forEntity qui définit l'entité JPA que notre référentiel doit gérer:

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

ou avec une classe abstraite:

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

Le module de données découvre les classes (ou interfaces) avec une telle annotation et il traitera les méthodes qui se trouvent à l'intérieur.

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

The first possibility to define a query is to use method name which follows a defined naming convention.

It looks like below:

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

Next, we'll focus on each part of this definition.

4.1. Return Type

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:

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.

public abstract Collection 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.

The above query will return all Users with a first name matching even the method name prefix suggests something different.

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.

The next section shows more details about method name prefix.

4.2. Prefix for Query Method

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:

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:

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.

The next section shows how we can add more attributes to the queries.

4.3. Query With Many Properties

In the query, we can use many properties combined with and operators.

public abstract Collection findByFirstNameAndLastName( String firstName, String lastName); public abstract Collection 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

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:

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

4.5. Order in the Query

DeltaSpike allows us to define an order in which result should be returned. We can define both – ascending and descending order:

public abstract List 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 findAllOrderByFirstNameAscLastNameDesc(); 

Next, we'll show how to limit the query result size.

4.6. Limit Query Result Size and Pagination

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 findTop2OrderByFirstNameAsc(); public abstract Collection findFirst2OrderByFirstNameAsc();

First and top can be used interchangeably.

We can then enable query pagination by providing two additional parameters: @FirstResult and @MaxResult:

public abstract Collection 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.

In the next section, we'll focus on how to do this.

5. Basic Repository Types

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.:

@Repository public interface UserRepository extends FullEntityRepository { // ... }

Or using an abstract class:

@Repository public abstract class UserRepository extends AbstractEntityRepository { // ... } 

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.

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:

public List 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.

The last possibility to create a query is to use @Query annotation which we will show next.

6. @Query Annotation

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.

By default this is a JPQL query:

@Query("select u from User u where u.firstName = ?1") public abstract Collection 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:

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

7. Conclusion

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.

We explored how to implement the repository pattern. We also introduced three possibilities how to define a query to execute.

Comme toujours, les exemples de code complets utilisés dans cet article sont disponibles à l'adresse over sur Github.