Guide du module de données DeltaSpike

Guide du module de données DeltaSpike

1. Vue d'ensemble

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

Of course, it can work with the different implementation of CDI - JBoss Weld ou OpenWebBeans. Il est également testé sur de nombreux serveurs d’applications.

Dans ce didacticiel, nous allons nous concentrer surone of the best known and useful – Data module.

2. Configuration du module de données DeltaSpike

Le module Apache DeltaSpike Data est utilisé poursimplify implementation of the repository pattern. Il autorisereducing a boilerplate code by providing centralized logic for queries creation and execution.

C'est très similaire au projetSpring 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. La mise en œuvre se fera pour nous par l'extension CDI.

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

2.1. Dépendances requises

Pour utiliser le module de données Apache DeltaSpike 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 de données Apache DeltaSpike sont disponibles sur Maven Central:

Àrun an application with Data module, we also need a JPA and CDI implementations available at runtime.

Bien qu'il soit possible d'exécuter Apache DeltaSpike dans l'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 sont entièrement compatibles avec Java 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 pourEntityManager.

2.2. Configuration de Entity Manager

LesData module requires EntityManager to be injected over 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 nomprimary défini dans le fichierpersistence.xml.

Voyons ci-dessous un exemple de définition:


   java:jboss/datasources/example-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

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. Nous pouvons le faire dans le fichierapache-deltaspike.properties (sous le répertoireMETA-INF):

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

Nous pouvons définir quatre types de stratégie de transaction:

  • BeanManagedUserTransactionStrategy

  • ResourceLocalTransactionStrategy

  • ContainerManagedTransactionStrategy

  • EnvironnementAwareTransactionStrategy

Tous implémententorg.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 la classeany abstract class or interface can become a repository du module de données Apache DeltaSpike.

All we have to do is toadd an @Repositoryannotation with un attributforEntity 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 traite les méthodes qui se trouvent à l'intérieur.

Il y a peu de possibilités de définir la requête à exécuter. Nous en parlerons un par un dans les sections suivantes.

4. Requête à partir du nom de la méthode

La première possibilité dedefine a query is to use method name which follows a defined naming convention.

Cela ressemble à ci-dessous:

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

Ensuite, nous nous concentrerons sur chaque partie de cette définition.

4.1. Type de retour

Lesreturn type mainly defines how many objects our query might return. Nous ne pouvons pas définir un type d'entité unique en tant que valeur de retour au cas où notre requête pourrait renvoyer plusieurs résultats.

La méthode suivante lèvera une exception au cas où il y aurait plus d'unUser avec un nom donné:

public abstract User findByFirstName(String firstName);

L’inverse n’est pas vrai - nous pouvons définir une valeur de retour comme unCollection même si le résultat ne sera qu’une seule entité.

public abstract Collection findAnyByFirstName(String firstName);

Le préfixe de nom de méthode qui suggère une valeur comme type de retour (par exemple,findAny) est supprimé au cas où nous définirions la valeur de retour commeCollection.

La requête ci-dessus renverra tous lesUsers avec un prénom correspondant même si le préfixe du nom de méthode suggère quelque chose de différent.

De telles combinaisons (le type de retour deCollection et un préfixe qui suggère un seul retour de valeur) doivent être évitées car le code devient peu intuitif et difficile à comprendre.

La section suivante montre plus de détails sur le préfixe du nom de la méthode.

4.2. Préfixe pour la méthode de requête

Prefix defines the action we want to execute on the repository. Le plus utile est de trouver des entités qui correspondent à des critères de recherche donnés.

Il existe de nombreux préfixes pour cette action tels quefindBy,findAny,findAll.  Pour la liste détaillée, veuillez consulter Apache DeltaSpikedocumentation officiel:

public abstract User findAnyByLastName(String lastName);

Cependant, il existe également desother method templates which are used for counting and removing entities. Nous pouvonscount toutes les lignes d'une table:

public abstract int count();

De plus, il existe un modèle de méthoderemove que nous pouvons ajouter dans notre référentiel:

public abstract void remove(User user);

La prise en charge des préfixes de méthodecountBy etremoveBy sera ajoutée dans la prochaine version d'Apache DeltaSpike 1.9.0.

La section suivante montre comment nous pouvons ajouter plus d'attributs aux requêtes.

4.3. Requête avec de nombreuses propriétés

Dans la requête, nous pouvons utilisermany properties combined with and operators.

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

Nous pouvons combiner autant de propriétés que nous voulons. La recherche de propriétés imbriquées est également disponible, ce que nous montrerons ensuite.

4.4. Requête avec des propriétés imbriquées

Lesquery can also use nested properties.

Dans l'exemple suivant, l'entitéUser a une propriété d'adresse de typeAddress et l'entitéAddress a une propriétécity:

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

4.5. Ordre dans la requête

DeltaSpike nous permet dedefine an order in which result should be returned. Nous pouvons définir les deux - ordre croissant et décroissant:

public abstract List findAllOrderByFirstNameAsc();

Comme indiqué ci-dessus tous leswe 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.

Nous pouvons combiner plusieurs commandes facilement:

public abstract List findAllOrderByFirstNameAscLastNameDesc();

Ensuite, nous montrerons comment limiter la taille des résultats de la requête.

4.6. Limiter la taille et la pagination des résultats de la requête

Il existe des cas d'utilisation lorsque nous voulons extraire quelques premières lignes de l'ensemble du résultat. C'est ce qu'on appelle la limite de requête. C'est également simple avec le module de données:

public abstract Collection findTop2OrderByFirstNameAsc();
public abstract Collection findFirst2OrderByFirstNameAsc();

First ettop peuvent être utilisés de manière interchangeable.

On peut alorsenable query pagination by providing two additional parameters: @FirstResult and @MaxResult:

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

Nous avons déjà défini beaucoup de méthodes dans le référentiel. Certains d'entre eux sont génériques et doivent être définis une fois et utilisés par chaque référentiel.

Apache DeltaSpike fournit quelques types de base que nous pouvons utiliser pour avoir beaucoup de méthodes prêtes à l'emploi.

Dans la section suivante, nous allons nous concentrer sur la manière de procéder.

5. Types de référentiel de base

Àget some basic repository methods, our repository should extend basic type provided by Apache DeltaSpike. Il y en a quelques-uns commeEntityRepository,FullEntityRepository, etc.:

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

Ou en utilisant une classe abstraite:

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

L'implémentation ci-dessus nous donne beaucoup de méthodes sans écrire de lignes de code supplémentaires. Nous avons donc obtenu ce que nous voulions: réduire considérablement le code standard.

Si nous utilisons le type de référentiel de base, il n'est pas nécessaire de transmettre une valeur d'attributforEntity supplémentaire à notre annotation@Repository.

Lorsque nous utilisons des classes abstraites au lieu d'interfaces pour nos référentiels, nous avons une possibilité supplémentaire de créer une requête personnalisée.

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();
}

Dans l'exemple ci-dessus, nous avons utilisé une méthode utilitairetypedQuery pour créer une implémentation personnalisée.

La dernière possibilité pour créer une requête est d'utiliser l'annotation@Query que nous montrerons ensuite.

6. Annotation@Query

Le SQLquery to execute can also be defined with the @Query annotation. C'est très similaire à la solution Spring. Nous devons ajouter une annotation à la méthode avec une requête SQL comme valeur.

Par défaut, il s'agit d'une requête JPQL:

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

Comme dans l'exemple ci-dessus, nous pouvons facilement transmettre des paramètres à la requête via un index.

Si nous voulons passer la requête via SQL natif au lieu de JPQL, nous devons définir un attribut de requête supplémentaire -isNative avec la valeur vraie:

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

7. Conclusion

Dans cet article, nous avons couvert la définition de base d’Apache DeltaSpike, et nous nous sommes concentrés sur la partie passionnante - module de données. C'est très similaire au Spring Data Project.

Nous avons exploré comment implémenter le modèle de référentiel. Nous avons également introduit trois possibilités pour définir une requête à exécuter.

Comme toujours, les exemples de code complets utilisés dans cet article sont disponiblesover on Github.