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