Una guida al modulo dati DeltaSpike

1. Panoramica

Apache DeltaSpike è un progetto che fornisce una raccolta di estensioni CDI per progetti Java; richiede un'implementazione CDI per essere disponibile in fase di esecuzione.

Naturalmente, può funzionare con la diversa implementazione di CDI - JBoss Weld o OpenWebBeans. È anche testato su molti server delle applicazioni.

In questo tutorial, ci concentreremo su uno dei più conosciuti e utili: il modulo Dati .

2. Configurazione del modulo dati DeltaSpike

Il modulo Apache DeltaSpike Data viene utilizzato per semplificare l'implementazione del modello di repository . Consente di ridurre un codice boilerplate fornendo una logica centralizzata per la creazione e l'esecuzione delle query .

È molto simile al progetto Spring Data. Per interrogare un database, dobbiamo definire una dichiarazione di metodo (senza implementazione) che segue la convenzione di denominazione definita o che contiene l' annotazione @Query . L'implementazione verrà eseguita per noi dall'estensione CDI.

Nelle prossime sottosezioni, tratteremo come configurare il modulo Apache DeltaSpike Data nella nostra applicazione.

2.1. Dipendenze richieste

Per utilizzare il modulo dati Apache DeltaSpike nell'applicazione, è necessario configurare le dipendenze richieste.

Quando Maven è il nostro strumento di compilazione dobbiamo usare:

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

Quando usiamo Gradle:

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

Gli artefatti del modulo dati Apache DeltaSpike sono disponibili su Maven Central:

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

Per eseguire un'applicazione con il modulo Data, abbiamo anche bisogno di implementazioni JPA e CDI disponibili in fase di runtime .

Sebbene sia possibile eseguire Apache DeltaSpike nell'applicazione Java SE, nella maggior parte dei casi verrà distribuito sull'Application Server (ad esempio, Wildfly o WebSphere).

I server delle applicazioni hanno il supporto completo di Jakarta EE, quindi non dobbiamo fare altro. In caso di applicazione Java SE, dobbiamo fornire queste implementazioni (ad esempio, aggiungendo dipendenze a Hibernate e JBoss Weld).

Successivamente, tratteremo anche la configurazione richiesta per EntityManager .

2.2. Configurazione di Entity Manager

Il modulo Dati richiede che EntityManager venga iniettato su CDI .

Possiamo ottenere ciò utilizzando un produttore CDI:

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

Il codice precedente presuppone che abbiamo un'unità di persistenza con nome primaria definita nel file persistence.xml .

Vediamo di seguito come esempio di definizione:

 java:jboss/datasources/baeldung-jee7-seedDS     

L'unità di persistenza nel nostro esempio utilizza il tipo di transazione JTA, il che significa che dobbiamo fornire una strategia di transazione che utilizzeremo.

2.3. Strategia di transazione

Nel caso in cui stiamo utilizzando il tipo di transazione JTA per la nostra origine dati, dobbiamo definire la strategia di transazione che verrà utilizzata nei repository Apache DeltaSpike . Possiamo farlo all'interno del file apache-deltaspike.properties (nella directory META-INF ):

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

Esistono quattro tipi di strategia di transazione che possiamo definire:

  • BeanManagedUserTransactionStrategy
  • ResourceLocalTransactionStrategy
  • ContainerManagedTransactionStrategy
  • EnvironmentAwareTransactionStrategy

Tutti implementano org.apache.deltaspike.jpa.spi.transaction.TransactionStrategy .

Questa era l'ultima parte della configurazione richiesta per il nostro modulo dati.

Successivamente, mostreremo come implementare le classi di pattern del repository.

3. Classi di repository

Quando utilizziamo il modulo dati Apache DeltaSpike, qualsiasi classe o interfaccia astratta può diventare una classe di repository .

Tutto quello che dobbiamo fare è aggiungere un'annotazione @Repository con un attributo forEntity che definisce l'entità JPA che il nostro repository dovrebbe gestire:

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

o con una classe astratta:

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

Il modulo dati scopre classi (o interfacce) con tale annotazione ed elaborerà i metodi che si trovano all'interno.

Ci sono poche possibilità per definire la query da eseguire. Ne parleremo uno per uno a breve nelle sezioni seguenti.

4. Query dal nome del metodo

La prima possibilità per definire una query è utilizzare il nome del metodo che segue una convenzione di denominazione definita .

Sembra di seguito:

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

Successivamente, ci concentreremo su ciascuna parte di questa definizione.

4.1. Tipo di ritorno

Il tipo restituito definisce principalmente quanti oggetti la nostra query potrebbe restituire . Non possiamo definire un singolo tipo di entità come valore restituito nel caso in cui la nostra query possa restituire più di un risultato.

Il metodo seguente genererà un'eccezione nel caso in cui sia presente più di un utente con il nome dato:

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.

Come sempre, gli esempi di codice completi utilizzati in questo articolo sono disponibili su Github.