You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by th...@apache.org on 2013/12/03 18:17:19 UTC
svn commit: r1547483 - /deltaspike/site/trunk/content/data.mdtext
Author: thug
Date: Tue Dec 3 17:17:19 2013
New Revision: 1547483
URL: http://svn.apache.org/r1547483
Log:
Data module documentation initial version.
Added:
deltaspike/site/trunk/content/data.mdtext (with props)
Added: deltaspike/site/trunk/content/data.mdtext
URL: http://svn.apache.org/viewvc/deltaspike/site/trunk/content/data.mdtext?rev=1547483&view=auto
==============================================================================
--- deltaspike/site/trunk/content/data.mdtext (added)
+++ deltaspike/site/trunk/content/data.mdtext Tue Dec 3 17:17:19 2013
@@ -0,0 +1,1127 @@
+Title: Data Module
+Notice: Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+ .
+ http://www.apache.org/licenses/LICENSE-2.0
+ .
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+[TOC]
+
+***
+
+# Introduction
+
+The repository pattern used to be one of the core J2EE patterns and could be found in
+most enterprise applications reading and writing data to persistent stores.
+While the Java Persistence API (JPA) as part of Java EE 5+ has replaced many aspects of the
+repository pattern, it is still a good approach to centralize complex query logic related to
+specific entities.
+
+The DeltaSpike Data module is intended to help you simplifying your repository layer.
+While you will have complex queries in a repository requiring your full attention,
+there will also be many simple ones often requiring boilerplate code and clutter.
+This is where the DeltaSpike data module will help you keeping your repository lean so you
+can focus on the though things.
+
+The code sample below will give you a quick overview on the common usage scenarios of the data module:
+
+ :::java
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ List<Person> findByAgeBetweenAndGender(int minAge, int maxAge, Gender gender);
+
+ @Query("select p from Person p where p.ssn = ?1")
+ Person findBySSN(String ssn);
+
+ @Query(named=Person.BY_FULL_NAME)
+ Person findByFullName(String firstName, String lastName);
+
+ }
+
+As you see in the sample, there are several usage scenarios outlined here:
+
+* Declare a method which executes a query by simply translating its name and parameters into a query.
+* Declare a method which automatically executes a given JPQL query string with parameters.
+* Declare a method which automatically executes a named query with parameters.
+
+The implementation of the method is done automatically by the CDI extension.
+A client can declare a dependency to the interface only. The details on how to use those
+features are outlines in the following chapters.
+
+# Installation
+
+## Prerequisites
+
+The simplest way using the DeltaSpike data module is to run your application in a Java EE container
+supporting at least the Java EE 6 Web Profile. Other configurations like running it inside Tomcat or
+even a Java SE application should be possible - you need to include a JPA provider as well as a CDI container
+to your application manually.
+
+Also note that in order to use abstract classes as repositories, this currently requires the presence
+of the http://www.javassist.org[javassist] library in your classpath.
+
+**CAUTION:**
+
+> Using DeltaSpike data in an EAR deployment is currently restricted to annotation-based entities.
+
+
+## Maven Dependency Configuration
+
+If you are using Maven as your build tool, you can add the following dependencies to your +pom.xml+
+file to include the DeltaSpike data module:
+
+ <dependency>
+ <groupId>org.apache.deltaspike.modules</groupId>
+ <artifactId>deltaspike-data-module-api</artifactId>
+ <version>${deltaspike.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.deltaspike.modules</groupId>
+ <artifactId>deltaspike-data-module-impl</artifactId>
+ <version>${deltaspike.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+
+**TIP:**
+
+> Substitute the expression `${deltaspike.version}` with the most recent or appropriate version
+> of DeltaSpike. Alternatively, you can create a Maven user-defined property to satisfy this
+> substitution so you can centrally manage the version.
+
+Including the API at compile time and only include the implementation at runtime protects you from
+inadvertantly depending on an implementation class.
+
+## Setup your application
+
+DeltaSpike data requires an `EntityManager` exposed via a CDI producer - which is common practice
+in Java EE 6 applications.
+
+ :::java
+ public class DataSourceProducer
+ {
+
+ @PersistenceUnit
+ private EntityManagerFactory emf;
+
+ @Produces
+ public EntityManager create()
+ {
+ return emf.createEntityManager();
+ }
+
+ public void close(@Disposes EntityManager em)
+ {
+ if (em.isOpen())
+ {
+ em.close();
+ }
+ }
+
+ }
+
+
+This allows the `EntityManager` to be injected over CDI instead of only being used with a
+`@PersistenceContext` annotation. Using multiple `EntityManager` is explored in more detail
+in a following section.
+
+You're now ready to use repositories in your application!
+
+# Core Concepts
+
+## Repositories
+
+With the DeltaSpike data module, it is possible to make a repository out of basically any
+abstract class or interface (using a concrete class will work too, but you won't be able to use
+most of the CDI extension features). All that is required is to mark the type as such with a
+simple annotation:
+
+ :::java
+ @Repository(forEntity = Person.class)
+ public abstract class PersonRepository {
+ ...
+ }
+
+ @Repository(forEntity = Person.class)
+ public interface PersonRepository {
+ ...
+ }
+
+
+The `@Repository` annotation tells the extension that this is a repository for the `Person` entity.
+Any method defined on the repository will be processed by the framework. The annotation does not
+require to set the entity class (we'll see later why) but if there are just plain classes or
+interfaces this is the only way to tell the framework what entity the repository relates to.
+In order to simplify this, DeltaSpike data provides several base types.
+
+### The `EntityRepository` interface
+
+Although mainly intended to hold complex query logic, working with both a repository and an `EntityManager`
+in the service layer might unnecessarily clutter code. In order to avoid this for the most common cases,
+DeltaSpike Data provides base types which can be used to replace the entity manager.
+
+The top base type is the `EntityRepository` interface, providing common methods used with an `EntityManager`.
+The following code shows the most important methods of the interface:
+
+ :::java
+ public interface EntityRepository<E, PK extends Serializable>
+ {
+
+ E save(E entity);
+
+ void remove(E entity);
+
+ void refresh(E entity);
+
+ void flush();
+
+ E findBy(PK primaryKey);
+
+ List<E> findAll();
+
+ List<E> findBy(E example, SingularAttribute<E, ?>... attributes);
+
+ List<E> findByLike(E example, SingularAttribute<E, ?>... attributes);
+
+ Long count();
+
+ Long count(E example, SingularAttribute<E, ?>... attributes);
+
+ Long countLike(E example, SingularAttribute<E, ?>... attributes);
+
+ }
+
+The concrete repository can then extend this basic interface. For our Person repository,
+this might look like the following:
+
+ :::java
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ Person findBySsn(String ssn);
+
+ }
+
+
+**TIP:**
+
+> Annotations on interfaces do not inherit. If the `EntityRepository` interface is extended by another
+> interface adding some more common methods, it is not possible to simply add the annotation there.
+> It needs to go on each concrete repository. The same is not true if a base class is introduced,
+> as we see in the next chapter.
+
+### The `AbstractEntityRepository` class
+
+This class is an implementation of the `EntityRepository` interface and provides additional functionality
+when custom query logic needs also to be implemented in the repository.
+
+ :::java
+ public abstract class PersonRepository extends AbstractEntityRepository<Person, Long>
+ {
+
+ public Person findBySSN(String ssn)
+ {
+ return getEntityManager()
+ .createQuery("select p from Person p where p.ssn = ?1", Person.class)
+ .setParameter(1, ssn)
+ .getResultList();
+ }
+
+ }
+
+
+## Using Multiple `EntityManager`
+
+While most applications will run just fine with a single `EntityManager`, there might be setups
+where multiple data sources are used. This can be configured with the `EntityManagerConfig` annotation:
+
+ :::java
+ @Repository
+ @EntityManagerConfig(entityManagerResolver = CrmEntityManagerResolver.class, flushMode = FlushModeType.COMMIT)
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+ ...
+ }
+
+ public class CrmEntityManagerResolver implements EntityManagerResolver
+ {
+ @Inject @CustomerData // Qualifier - assumes a producer is around...
+ private EntityManager em;
+
+ @Override
+ public EntityManager resolveEntityManager()
+ {
+ return em;
+ }
+ }
+
+
+Again, note that annotations on interfaces do not inherit, so it's not possible to create something like a base
+`CrmRepository` interface with the `@EntityManagerConfig` and then extending / implementing this interface.
+
+
+# Query Method Expressions
+
+Good naming is a difficult aspects in software engineering. A good method name usually makes
+comments unnecessary and states exactly what the method does. And with method expressions, the
+method name is actually the implementation!
+
+## Using method expressions
+
+Let's start by looking at a (simplified for readability) example:
+
+ :::java
+ @Entity
+ public class Person
+ {
+
+ @Id @GeneratedValue
+ private Long id;
+ private String name;
+ private Integer age;
+ private Gender gender;
+
+ }
+
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ List<Person> findByNameLikeAndAgeBetweenAndGender(String name,
+ int minAge, int maxAge, Gender gender);
+
+ }
+
+
+Looking at the method name, this can easily be read as query all Persons which have a name like
+the given name parameter, their age is between a min and a max age and having a specific gender.
+The DeltaSpike module can translate method names following a given format and directly generate
+the query implementation out of it (in EBNF-like form):
+
+
+ (Entity|List<Entity>) findBy(Property[Comparator]){Operator Property [Comparator]}
+
+
+Or in more concrete words:
+
+* The query method must either return an entity or a list of entities.
+* It must start with the `findBy` keyword (or related `findOptionalBy`, `findAnyBy`).
+* Followed by a property of the Repository entity and an optional comparator (we'll define this later).
+ The property will be used in the query together with the comparator. Note that the number of arguments
+ passed to the method depend on the comparator.
+* You can add more blocks of property-comparator which have to be concatenated by a boolean operator.
+ This is either an `And` or `Or`.
+
+Other assumptions taken by the expression evaluator:
+
+* The property name starts lower cased while the property in the expression has an upper cases first character.
+
+Following comparators are currently supported to be used in method expressions:
+
+<table>
+ <thead>
+ <tr>
+ <td>Name</td><td># of Arguments</td><td>Description</td>
+ </tr>
+ </thead>
+ <tr><td>Equal</td> <td>1</td><td>Property must be equal to argument value. If the operator is omitted in the expression, this is assumed as default.</td></tr>
+ <tr><td>NotEqual</td> <td>1</td><td>Property must be not equal to argument value.</td></tr>
+ <tr><td>Like</td> <td>1</td><td>Property must be like the argument value. Use the %-wildcard in the argument.</td></tr>
+ <tr><td>GreaterThan</td> <td>1</td><td>Property must be greater than argument value.</td></tr>
+ <tr><td>GreaterThanEquals</td><td>1</td><td>Property must be greater than or equal to argument value.</td></tr>
+ <tr><td>LessThan</td> <td>1</td><td>Property must be less than argument value.</td></tr>
+ <tr><td>LessThanEquals</td> <td>1</td><td>Property must be less than or equal to argument value.</td></tr>
+ <tr><td>Between</td> <td>2</td><td>Property must be between the two argument values.</td></tr>
+ <tr><td>IsNull</td> <td>0</td><td>Property must be null.</td></tr>
+ <tr><td>IsNotNull</td> <td>0</td><td>Property must be non-null.</td></tr>
+</table>
+
+Note that DeltaSpike will validate those expressions during startup, so you will notice early in case you have a typo
+in those expressions.
+
+## Query Ordering
+
+Beside comparators it's also possible to sort queries by using the `OrderBy` keyword, followed
+by the attribute name and the direction (`Asc` or `Desc`).
+
+ :::java
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ List<Person> findByLastNameLikeOrderByAgeAscLastNameDesc(String lastName);
+
+ }
+
+## Nested Properties
+
+To create a comparison on a nested property, the traversal parts can be separated by a `_`:
+
+ ::java
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ List<Person> findByCompany_companyName(String companyName);
+
+ }
+
+## Query Options
+
+DeltaSpike supports query options on method expressions. If you want to page a query,
+you can change the first result as well as the maximum number of results returned:
+
+ :::java
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ List<Person> findByNameLike(String name, @FirstResult int start, @MaxResults int pageSize);
+
+ }
+
+## Method Prefix
+
+In case the `findBy` prefix does not comply with your team conventions, this can be adapted:
+
+ :::java
+ @Repository(methodPrefix = "fetchWith")
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ List<Person> fetchWithNameLike(String name, @FirstResult int start, @MaxResults int pageSize);
+
+ }
+
+# Query Annotations
+
+While method expressions are fine for simple queries, they will often reach their limit once things
+get slightly more complex. Another aspect is the way you want to use JPA: The recommended approach
+using JPA for best performance is over named queries. To help incorporate those use cases, the
+DeltaSpike data module supports also annotating methods for more control on the generated query.
+
+## Using Query Annotations
+
+The simples way to define a specific query is by annotating a method and providing the JPQL query
+string which has to be executed. In code, this looks like the following sample:
+
+ :::java
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ @Query("select count(p) from Person p where p.age > ?1")
+ Long countAllOlderThan(int minAge);
+
+ }
+
+The parameter binding in the query corresponds to the argument index in the method.
+
+You can also refer to a named query which is constructed and executed automatically. The `@Query`
+annotation has a named attribute which corresponds to the query name:
+
+ :::java
+ @Entity
+ @NamedQueries({
+ @NamedQuery(name = Person.BY_MIN_AGE,
+ query = "select count(p) from Person p where p.age > ?1 order by p.age asc")
+ })
+ public class Person
+ {
+
+ public static final String BY_MIN_AGE = "person.byMinAge";
+ ...
+
+ }
+
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ @Query(named = Person.BY_MIN_AGE)
+ Long countAllOlderThan(int minAge);
+
+ }
+
+Same as before, the parameter binding corresponds to the argument index in the method. If the named
+query requires named parameters to be used, this can be done by annotating the arguments with the
+`@QueryParam` annotation.
+
+**TIP:**
+
+> Java does not preserve method parameter names (yet), that's why the annotation is needed.
+
+ :::java
+ @NamedQuery(name = Person.BY_MIN_AGE,
+ query = "select count(p) from Person p where p.age > :minAge order by p.age asc")
+
+ ...
+
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ @Query(named = Person.BY_MIN_AGE)
+ Long countAllOlderThan(@QueryParam("minAge") int minAge);
+
+ }
+
+It is also possible to set a native SQL query in the annotation. The `@Query` annotation has a native attribute
+which flags that the query is not JPQL but plain SQL:
+
+ :::java
+ @Entity
+ @Table(name = "PERSON_TABLE")
+ public class Person
+ {
+ ...
+ }
+
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ @Query(value = "SELECT * FROM PERSON_TABLE p WHERE p.AGE > ?1", isNative = true)
+ List<Person> findAllOlderThan(int minAge);
+
+ }
+
+## Annotation Options
+
+Beside providing a query string or reference, the `@Query` annotation provides also two more attributes:
+
+ :::java
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ @Query(named = Person.BY_MIN_AGE, max = 10, lock = LockModeType.PESSIMISTIC_WRITE)
+ List<Person> findAllForUpdate(int minAge);
+
+ }
+
+<table>
+ <thead>
+ <tr>
+ <td>Name</td><td>Description</td>
+ </tr>
+ </thead>
+ <tr><td>max</td> <td>Limits the number of results.</td></tr>
+ <tr><td>lock</td><td>Use a specific LockModeType to execute the query.</td></tr>
+</table>
+
+Note that these options can also be applied to method expressions.
+
+## Query Options
+
+All the query options you have seen so far are more or less static. But sometimes you might want
+to apply certain query options dynamically. For example, sorting criteria could come from a user
+selection so they cannot be known beforehand. DeltaSpike allows you to apply query options at runtime by
+using the `QueryResult` result type:
+
+ :::java
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ @Query("select p from Person p where p.age between ?1 and ?2")
+ QueryResult<Person> findAllByAge(int minAge, int maxAge);
+
+ }
+
+Once you have obtained a `QueryResult`, you can apply further options to the query:
+
+ :::java
+ List<Person> result = personRepository.findAllByAge(18, 65)
+ .sortAsc(Person_.lastName)
+ .sortDesc(Person_.age)
+ .lockMode(LockModeType.WRITE)
+ .hint("org.hibernate.timeout", Integer.valueOf(10))
+ .getResultList();
+
+**CAUTION:**
+
+> Note that sorting is only applicable to method expressions or non-named queries. For named queries it might be possible, but is currently only supported for Hibernate, EclipseLink and OpenJPA.
+
+Note that the `QueryResult` return type can also be used with method expressions.
+
+## Pagination
+
+We introduced the `QueryResult` type in the last chapter, which can also be used for pagination:
+
+ :::java
+ // Query API style
+ QueryResult<Person> paged = personRepository.findByAge(age)
+ .maxResults(10)
+ .firstResult(50);
+
+ // or paging style
+ QueryResult<Person> paged = personRepository.findByAge(age)
+ .withPageSize(10) // equivalent to maxResults
+ .toPage(5);
+
+ int totalPages = paged.countPages();
+
+## Bulk Operations
+
+While reading entities and updating them one by one might be fine for many use cases, applying bulk
+updates or deletes is also a common usage scenario for repositories. DeltaSpike supports this with a special
+marking annotation `@Modifying`:
+
+ :::java
+ @Repository
+ public interface PersonRepository extends EntityRepository<Person, Long>
+ {
+
+ @Modifying
+ @Query("update Person as p set p.classifier = ?1 where p.classifier = ?2")
+ int updateClassifier(Classifier current, Classifier next);
+
+ }
+
+Bulk operation query methods can either return void or int, which counts the number of entities affected
+by the bulk operation.
+
+## Optional Query Results
+
+The JPA spec requires to throw exceptions in case the `getSingleResult()` method does either return
+no or more than one result. This can result in tedious handling with try-catch blocks or have potential
+impact on your transaction (as the `RuntimeException` might roll it back).
+
+DeltaSpike Data gives the option to change this to the way it makes most sense for the current usecase.
+While the default behavior is still fully aligned with JPA, it's also possible to request optional query results.
+
+## Zero or One Result
+
+With this option, the query returns `null` instead of throwing a `NoResultException` when there is no
+result returned. It's usable with method expressions, `Query` annotations and `QueryResult<E>` calls.
+
+ :::java
+ @Repository(forEntity = Person.class)
+ public interface PersonRepository
+ {
+
+ Person findOptionalBySsn(String ssn);
+
+ @Query(named = Person.BY_NAME, singleResult = SingleResultType.OPTIONAL)
+ Person findByName(String firstName, String lastName);
+
+ }
+
+For method expressions, the `findOptionalBy` prefix can be used. For `@Query` annotations, the `singleResult`
+attribute can be overridden with the `SingleResultType.OPTIONAL` enum.
+
+In case the query returns more than one result, a `NonUniqueResultException` is still thrown.
+
+## Any Result
+
+If the caller does not really mind what kind if result is returned, it's also possible to request any
+result from the query. If there is no result, same as for optional queries `null` is returned. In case
+there is more than one result, any result is returned, or more concretely the first result out of the
+result list.
+
+ :::java
+ @Repository(forEntity = Person.class)
+ public interface PersonRepository
+ {
+
+ Person findAnyByLastName(String lastName);
+
+ @Query(named = Person.BY_NAME, singleResult = SingleResultType.ANY)
+ Person findByName(String firstName, String lastName);
+
+ }
+
+For method expressions, the `findAnyBy` prefix can be used. For `@Query` annotations, the `singleResult`
+attribute can be overridden with the `SingleResultType.ANY` enum.
+
+This option will not throw an exception.
+
+# Extensions
+
+## Query Delegates
+
+While repositories defines several base interfaces, there might still be the odd convenience
+method that is missing. This is actually intentional - things should not get overloaded for each and
+every use case. That's why in DeltaSpike you can define your own reusable methods.
+
+For example, you might want to use the QueryDsl library in your repositories:
+
+ :::java
+ import com.mysema.query.jpa.impl.JPAQuery;
+
+ public interface QueryDslSupport
+ {
+ JPAQuery jpaQuery();
+ }
+
+ @Repository(forEntity = Person.class)
+ public interface PersonRepository extends QueryDslSupport
+ {
+ ...
+ }
+
+## Implementing the Query Delegate
+
+The first step is to define an interface which contains the extra methods for your repositories
+(as shown above):
+
+ :::java
+ public interface QueryDslSupport
+ {
+ JPAQuery jpaQuery();
+ }
+
+As a next step, you need to provide an implementation for this interface once. It's also important
+that this implementation implements the `DelegateQueryHandler` interface (don't worry, this is just
+an empty marker interface):
+
+ :::java
+ public class QueryDslRepositoryExtension<E> implements QueryDslSupport, DelegateQueryHandler
+ {
+
+ @Inject
+ private QueryInvocationContext context;
+
+ @Override
+ public JPAQuery jpaQuery()
+ {
+ return new JPAQuery(context.getEntityManager());
+ }
+
+ }
+
+As you see in the sample, you can inject a `QueryInvocationContext` which contains utility methods
+like accessing the current `EntityManager` and entity class.
+
+Note that, if you define multiple extensions with equivalent method signatures, there is no specific
+order in which the implementation is selected.
+
+# Mapping
+
+While repositories are primarily intended to work with Entities, it might be preferable in some
+cases to have an additional mapping layer on top of them, e.g. because the Entities are quite complex
+but the service layer needs only a limited view on it, or because the Entities are exposed over a
+remote interface and there should not be a 1:1 view on the domain model.
+
+DeltaSpike Data allows to directly plugin in such a mapping mechanism without the need to specify additional
+mapping methods:
+
+ :::java
+ @Repository(forEntity = Person.class)
+ @MappingConfig(PersonDtoMapper.class)
+ public interface PersonRepository
+ {
+
+ PersonDto findBySsn(String ssn);
+
+ List<PersonDto> findByLastName(String lastName);
+
+ }
+
+The `PersonDtoMapper` class has to implement the `QueryInOutMapper` interface:
+
+ :::java
+ public class PersonDtoMapper implements QueryInOutMapper<Person>
+ {
+
+ @Override
+ public Object mapResult(Person result)
+ {
+ ... // converts Person into a PersonDto
+ }
+ ...
+
+ @Override
+ public Object mapResultList(List<Simple> result)
+ {
+ ... // result lists can also be mapped into something different
+ // than a collection.
+ }
+
+ @Override
+ public boolean mapsParameter(Object parameter)
+ {
+ return parameter != null && (
+ parameter instanceof PersonDto || parameter instanceof PersonId);
+ }
+
+ @Override
+ public Object mapParameter(Object parameter)
+ {
+ ... // converts query parameters if required
+ }
+ }
+
+The mapper can also be used to transform query parameters. Parameters are converted before
+executing queries and calling repository extensions.
+
+Note that those mapper classes are treated as CDI Beans, so it is possible to use injection
+in those beans (you might e.g. inject an `EntityManager` or other mappers). As the `@MappingConfig`
+refers to the mapper class directly, the mapper must be uniquely identifiable by its class.
+
+It's also possible to combine mappings with the base Repository classes:
+
+ :::java
+ @Repository(forEntity = Person.class)
+ @MappingConfig(PersonDtoMapper.class)
+ public interface PersonRepository extends EntityRepository<PersonDto, PersonId>
+ {
+ ...
+ }
+
+In this case, the `forEntity` attribute in the `@Repository` annotation is mandatory. Also it is up
+to the mapper to convert parameters correctly (in this example, a conversion from a `PersonDto`
+parameter to `Person` entity and from `PersonId` to `Long` is necessary).
+
+## Simple Mappings
+
+In many cases it's just required to map a DTO object back and forth. For this case, the `SimpleQueryInOutMapperBase` class
+can be subclassed, which only requires to override two methods:
+
+ :::java
+ public class PersonMapper extends SimpleQueryInOutMapperBase<Person, PersonDto>
+ {
+ @Override
+ protected PersonDto toDto(Person entity)
+ {
+ ...
+ }
+
+ @Override
+ protected Person toEntity(PersonDto dto) {
+ ...
+ }
+ }
+
+# JPA Criteria API Support
+
+Beside automatic query generation, the DeltaSpike data module also provides a DSL-like API to create JPA 2 Criteria queries.
+It takes advantage of the JPA 2 meta model, which helps creating type safe queries.
+
+**TIP:**
+
+> The JPA meta model can easily be generated with an annotation processor. Hibernate or EclipseLink
+> provide such a processor, which can be integrated into your compile and build cycle.
+
+Note that this criteria API is not intended to replace the standard criteria API - it's rather a utility
+API that should make life easier on the most common cases for a custom query. The JPA criteria API's
+strongest point is certainly its type safety - which comes at the cost of readability. We're trying to
+provide a middle way here. A less powerful API, but still type safe and readable.
+
+## API Usage
+
+The API is centered around the Criteria class and is targeted to provide a fluent interface
+to write criteria queries:
+
+ :::java
+ @Repository(forEntity = Person.class)
+ public abstract class PersonRepository implements CriteriaSupport<Person>
+ {
+
+ public List<Person> findAdultFamilyMembers(String name, Integer minAge)
+ {
+ return criteria()
+ .like(Person_.name, "%" + name + "%")
+ .gtOrEq(Person_.age, minAge)
+ .eq(Person_.validated, Boolean.TRUE)
+ .orderDesc(Person_.age)
+ .getResultList();
+ }
+
+ }
+
+Following comparators are supported by the API:
+
+<table>
+ <thead>
+ <tr>
+ <td>Name</td><td>Description</td>
+ </tr>
+ </thead>
+ <tr><td>.eq(..., ...) </td><td>Property value must be equal to the given value </td></tr>
+ <tr><td>.in(..., ..., ..., ...) </td><td>Property value must be in one of the given values. </td></tr>
+ <tr><td>.notEq(..., ...) </td><td>Negates equality </td></tr>
+ <tr><td>.like(..., ...) </td><td>A SQL `like` equivalent comparator. Use % on the value. </td></tr>
+ <tr><td>.notLike(..., ...) </td><td>Negates the like value </td></tr>
+ <tr><td>.lt(..., ...) </td><td>Property value must be less than the given value. </td></tr>
+ <tr><td>.ltOrEq(..., ...) </td><td>Property value must be less than or equal to the given value. </td></tr>
+ <tr><td>.gt(..., ...) </td><td>Property value must be greater than the given value. </td></tr>
+ <tr><td>.ltOrEq(..., ...) </td><td>Property value must be greater than or equal to the given value. </td></tr>
+ <tr><td>.between(..., ..., ...) </td><td>Property value must be between the two given values. </td></tr>
+ <tr><td>.isNull(...) </td><td>Property must be `null`</td></tr>
+ <tr><td>.isNotNull(...) </td><td>Property must be non-`null` </td></tr>
+ <tr><td>.isEmpty(...) </td><td>Collection property must be empty</td></tr>
+ <tr><td>.isNotEmpty(...) </td><td>Collection property must be non-empty</td></tr>
+</table>
+
+The query result can be modified with the following settings:
+
+<table>
+ <thead>
+ <tr>
+ <td>Name</td><td>Description</td>
+ </tr>
+ </thead>
+ <tr><td>.orderAsc(...)</td><td>Sorts the result ascending by the given property. Note that this can be applied to several properties</td></tr>
+ <tr><td>.orderDesc(...)</td><td>Sorts the result descending by the given property. Note that this can be applied to several properties</td></tr>
+ <tr><td>.distinct()</td><td>Sets distinct to true on the query.</td></tr>
+</table>
+
+Once all comparators and query options are applied, the `createQuery()` method is called.
+This creates a JPA TypedQuery object for the repository entity. If required, further processing can be applied here.
+
+## Joins
+
+For simple cases, restricting on the repository entity only works out fine, but once the data model
+gets more complicated, the query will have to consider relations to other entities. The module's criteria
+API therefore supports joins as shown in the sample below:
+
+ :::java
+ @Repository
+ public abstract class PersonRepository extends AbstractEntityRepository<Person, Long>
+ {
+
+ public List<Person> findByCompanyName(String companyName)
+ {
+ return criteria()
+ .join(Person_.company,
+ where(Company.class)
+ .eq(Company_.name, companyName)
+ )
+ .eq(Person_.validated, Boolean.TRUE)
+ .getResultList();
+ }
+
+ }
+
+Beside the inner and outer joins, also fetch joins are supported. Those are slighly simpler as seen in the next sample:
+
+ :::java
+ public abstract class PersonRepository extends AbstractEntityRepository<Person, Long>
+ {
+
+ public Person findBySSN(String ssn)
+ {
+ return criteria()
+ .fetch(Person_.familyMembers)
+ .eq(Person_.ssn, ssn)
+ .distinct()
+ .getSingleResult();
+ }
+
+ }
+
+## Boolean Operators
+
+By default, all query operators are concatenated as an and conjunction to the query. The DeltaSpike
+criteria API also allows to add groups of disjunctions.
+
+ :::java
+ public abstract class PersonRepository extends AbstractEntityRepository<Person, Long>
+ {
+
+ public List<Person> findAdults()
+ {
+ return criteria()
+ .or(
+ criteria().
+ .gtOrEq(Person_.age, 18)
+ .eq(Person_.origin, Country.SWITZERLAND),
+ criteria().
+ .gtOrEq(Person_.age, 21)
+ .eq(Person_.origin, Country.USA)
+ )
+ .getResultList();
+ }
+
+ }
+
+## Selections
+
+It might not always be appropriate to retrieve full entities - you might also be interested
+in scalar values or by modified entity attributes. The Criteria interface allows this with the
+selection method:
+
+ :::java
+ public abstract class PersonRepository extends AbstractEntityRepository<Person, Long>
+ {
+
+ public Statistics ageStatsFor(Segment segment)
+ {
+ return criteria()
+ .select(Statistics.class, avg(Person_.age), min(Person_.age), max(Person_.age))
+ .eq(Person_.segment, segment)
+ .getSingleResult();
+ }
+
+ public List<Object[]> personViewForFamily(String name)
+ {
+ return criteria()
+ .select(upper(Person_.name), attribute(Person_.age), substring(Person_.firstname, 1))
+ .like(Person_.name, name)
+ .getResultList();
+ }
+
+ }
+
+There are also several functions supported which can be used in the selection clause:
+
+<table>
+ <thead>
+ <tr>
+ <td>Name</td><td>Description</td>
+ </tr>
+ </thead>
+ <tr><td>abs(...)</td><td>Absolute value. Applicable to Number attributes.</td></tr>
+ <tr><td>avg(...)</td><td>Average value. Applicable to Number attributes.</td></tr>
+ <tr><td>count(...) </td><td>Count function. Applicable to Number attributes.</td></tr>
+ <tr><td>max(...) </td><td>Max value. Applicable to Number attributes.</td></tr>
+ <tr><td>min(...) </td><td>Min value. Applicable to Number attributes.</td></tr>
+ <tr><td>modulo(...)</td><td>Modulo function. Applicable to Integer attributes.</td></tr>
+ <tr><td>neg(...)</td><td>Negative value. Applicable to Number attributes.</td></tr>
+ <tr><td>sum(...) </td><td>Sum function. Applicable to Number attributes.</td></tr>
+ <tr><td>lower(...)</td><td>String to lowercase. Applicable to String attributes.</td></tr>
+ <tr><td>substring(int from, ...)</td><td>Substring starting from. Applicable to String attributes.</td></tr>
+ <tr><td>substring(int from, int to, ...)</td><td>Substring starting from ending to. Applicable to String attributes.</td></tr>
+ <tr><td>upper(...) </td><td>String to uppercase. Applicable to String attributes.</td></tr>
+ <tr><td>currDate() </td><td>The DB sysdate. Returns a Date object.</td></tr>
+ <tr><td>currTime() </td><td>The DB sysdate. Returns a Time object.</td></tr>
+ <tr><td>currTStamp()</td><td>The DB sysdate. Returns a Timestamp object. </td></tr>
+</table>
+
+# Auditing
+
+A common requirement for entities is tracking what is being done with them. DeltaSpike provides
+a convenient way to support this requirement.
+
+**TIP:**
+
+> DeltaSpike does not support creating revisions of entities. If this is a requirement for your audits,
+> have a look at Hibernate Envers.
+
+## Activating Auditing
+
+DeltaSpike uses an entity listener to update auditing data before entities get created or update.
+The entity listener must be activated before it can be used. This can either be done globally for
+all entities of a persistent unit or per entity.
+
+Activation per persistence unit in `orm.xml`:
+
+ <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" version="2.0">
+ <persistence-unit-metadata>
+ <persistence-unit-defaults>
+ <entity-listeners>
+ <entity-listener class="org.apache.deltaspike.data.impl.audit.AuditEntityListener" />
+ </entity-listeners>
+ </persistence-unit-defaults>
+ </persistence-unit-metadata>
+ </entity-mappings>
+
+Activation per entity:
+
+ :::java
+ @Entity
+ @EntityListeners(AuditEntityListener.class)
+ public class AuditedEntity
+ {
+
+ ...
+
+ }
+
+Note that for this variant, you need a compile dependency on the impl module. Alternatively, also the per
+entity listener can be configured by XML.
+
+## Using Auditing Annotations
+
+All that has to be done now is annotating the entity properties which are used to audit the entity.
+
+### Updating Timestamps
+
+To keep track on creation and modification times, following annotations can be used:
+
+ :::java
+ @Entity
+ public class AuditedEntity
+ {
+
+ ...
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @CreatedOn
+ private Date created;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @ModifiedOn
+ private Date updated;
+
+ ...
+
+ }
+
+In case the modification date should also be set during entity creation, the annotation can be customized:
+
+ :::java
+ @ModifiedOn(setOnCreate=true)
+
+### Who's Changing My Entities?
+
+Beside keeping track of when a change has happened, it's also often critical to track who's responsible
+for the change. Annotate a user tracking field with the following annotation:
+
+ :::java
+ @Entity
+ public class AuditedEntity
+ {
+
+ ...
+
+ @ModifiedBy
+ private String auditUser;
+
+ ...
+
+ }
+
+Now a little help is needed. The entity listener needs to be able to resolve the current user -
+there must be a bean available of the matching type for the annotation property, exposed over a special CDI qualifier:
+
+ :::java
+ public class UserProvider
+ {
+
+ @Inject
+ private User user;
+
+ @Produces @CurrentUser
+ public String currentUser() {
+ return user.getUsername();
+ }
+
+ ...
+
+ }
+
+**TIP:**
+
+> The JPA Spec does not recommend to modify entity relations from within a lifecycle callback.
+> If you expose another entity here, make sure that your persistence provider supports this. Also you
+> should ensure that the entity is attached to a persistent context. Also, be aware that the CDI container
+> will proxy a scoped bean, which might confuse the persistence provider when persisting / updating the
+> target entity.
\ No newline at end of file
Propchange: deltaspike/site/trunk/content/data.mdtext
------------------------------------------------------------------------------
svn:eol-style = native