RepositoryService
Collects together methods for creating, persisting and searching for entities from the underlying persistence store.
Typically it’s good practice to define a domain-specific service (eg CustomerRepository
) which then delegates to this service. This domain-specific service can use some RepositoryService 's "naive" methods that use client-side predicates for filtering; these can then be replaced by more sophisticated implementations that use proper server-side filtering later on without impacting the rest of the application.
API
interface RepositoryService {
EntityState getEntityState(Object object) (1)
T detachedEntity(T entity) (2)
T execInBulk(Callable<T> callable) (3)
T persist(T domainObject) (4)
T persistAndFlush(T domainObject) (5)
void persistAndFlush(Object... domainObjects) (6)
void remove(Object domainObject) (7)
void removeAndFlush(Object domainObject) (8)
void removeAll(Class<T> cls) (9)
List<T> allInstances(Class<T> ofType) (10)
List<T> allInstances(Class<T> ofType, long start, long count) (11)
List<T> allMatches(Class<T> ofType, Predicate<? super T> predicate) (12)
List<T> allMatches(Class<T> ofType, Predicate<? super T> predicate, long start, long count) (13)
List<T> allMatches(Query<T> query) (14)
Optional<T> uniqueMatch(Class<T> ofType, Predicate<T> predicate) (15)
Optional<T> uniqueMatch(Query<T> query) (16)
Optional<T> firstMatch(Class<T> ofType, Predicate<T> predicate) (17)
Optional<T> firstMatch(Query<T> query) (18)
T refresh(T pojo) (19)
T detach(T entity) (20)
}
1 | getEntityState(Object)
Returns the EntityState of given object . |
2 | detachedEntity(T)
Usually called as a precursor to persisting a domain entity, this method verifies that the object is an entity and injects domain services into it. |
3 | execInBulk(Callable)
Executes the passed in callable in bulk mode, meaning that the transaction will not be flushed within. |
4 | persist(T)
Persist the specified object (or do nothing if already persistent). |
5 | persistAndFlush(T)
Persist the specified object (or do nothing if already persistent) and flushes changes to the database. |
6 | persistAndFlush(Object)
Persist the specified objects (or do nothing if already persistent) and flushes changes to the database. |
7 | remove(Object)
Remove (ie delete) an object from the persistent object store (or do nothing if it has already been deleted). |
8 | removeAndFlush(Object)
Deletes the domain object but only if is persistent, and flushes changes to the database (meaning the object is deleted immediately). |
9 | removeAll(Class)
Removes all instances of the domain object. |
10 | allInstances(Class)
Returns all persisted instances of specified type (including subtypes). |
11 | allInstances(Class, long, long)
Overload of #allInstances(Class) , returns a page of persisted instances of specified type (including subtypes). |
12 | allMatches(Class, Predicate)
Returns all the instances of the specified type (including subtypes) that the predicate object accepts. |
13 | allMatches(Class, Predicate, long, long)
Overload of #allMatches(Class, Predicate) , returns a page of persisted instances of specified type (including subtypes). |
14 | allMatches(Query)
Returns all the instances that match the given Query . |
15 | uniqueMatch(Class, Predicate)
Finds the only instance of the specified type (including subtypes) that satifies the (client-side) predicate. |
16 | uniqueMatch(Query)
Find the only instance that matches the provided Query . |
17 | firstMatch(Class, Predicate)
Find the only instance of the specified type (including subtypes) that satifies the provided (client-side) predicate. |
18 | firstMatch(Query)
Find the only instance that matches the provided Query , if any. |
19 | refresh(T)
Reloads the domain entity from the database. |
20 | detach(T)
Explicitly detaches the entity from the current persistence session. |
Members
getEntityState(Object)
Returns the EntityState of given object .
detachedEntity(T)
Usually called as a precursor to persisting a domain entity, this method verifies that the object is an entity and injects domain services into it.
This approach allows the domain entity to have regular constructor (with parameters) to set up the initial state of the domain object. This is preferred over #detachedEntity(Class) , which also instantiates the class and then injects into it - but requires that the domain object has a no-arg constructor to do so.
This is the same functionality as exposed by org.apache.causeway.applib.services.factory.FactoryService#detachedEntity(Object) . It is provided in this service as a convenience because instantiating and #persist(Object) persisting an object are often done together.
execInBulk(Callable)
Executes the passed in callable in bulk mode, meaning that the transaction will not be flushed within.
Used for example by the audit trail extension, as a performance optimization.
persist(T)
Persist the specified object (or do nothing if already persistent).
The persist isn’t necessarily performed immediately; by default all pending changes are flushed to the database when the transaction completes.
persistAndFlush(T)
Persist the specified object (or do nothing if already persistent) and flushes changes to the database.
Flushing will also result in ORM-maintained bidirectional relationships being updated.
persistAndFlush(Object)
Persist the specified objects (or do nothing if already persistent) and flushes changes to the database.
Flushing will also result in ORM-maintained bidirectional relationships being updated.
remove(Object)
Remove (ie delete) an object from the persistent object store (or do nothing if it has already been deleted).
The delete isn’t necessarily performed immediately; by default all pending changes are flushed to the database when the transaction completes.
Note that this method is also a no-op if the domain object is not attached.
removeAndFlush(Object)
Deletes the domain object but only if is persistent, and flushes changes to the database (meaning the object is deleted immediately).
Flushing will also result in ORM-maintained bidirectional relationships being updated.
removeAll(Class)
Removes all instances of the domain object.
Intended primarily for testing purposes.
allInstances(Class)
Returns all persisted instances of specified type (including subtypes).
Intended primarily for prototyping purposes, though is safe to use in production applications to obtain all instances of domain entities if the number is known to be small (for example, reference/lookup data).
If there are no instances the list will be empty.
allInstances(Class, long, long)
Overload of #allInstances(Class) , returns a page of persisted instances of specified type (including subtypes).
If the optional range parameters are used, the dataset returned starts from (0 based) index, and consists of only up to count items.
allMatches(Class, Predicate)
Returns all the instances of the specified type (including subtypes) that the predicate object accepts.
If there are no instances the list will be empty. This method creates a new List object each time it is called so the caller is free to use or modify the returned List , but the changes will not be reflected back to the repository.
This method is useful during exploration/prototyping, but - because the filtering is performed client-side - this method is only really suitable for initial development/prototyping, or for classes with very few instances. Use #allMatches(Query) for production code.
allMatches(Class, Predicate, long, long)
Overload of #allMatches(Class, Predicate) , returns a page of persisted instances of specified type (including subtypes).
If the optional range parameters are used, the dataset considered (before filtering) starts from (0 based) index, runs through up to count items.
allMatches(Query)
Returns all the instances that match the given Query .
This is the main API for server-side (performant) queries returning multiple instances, where a org.apache.causeway.applib.query.NamedQuery can be passed in that ultimately describes a SELECT query with WHERE predicates. The mechanism by which this is defined depends on the ORM (JDO or JPA). A org.apache.causeway.applib.query.NamedQuery can optionally specify a org.apache.causeway.applib.query.NamedQuery#withRange(QueryRange) range of instances to be returned.
It is also possible to specify an org.apache.causeway.applib.query.AllInstancesQuery . This is equivalent to using #allInstances(Class, long, long) ; a range can also be specified.
uniqueMatch(Class, Predicate)
Finds the only instance of the specified type (including subtypes) that satifies the (client-side) predicate.
This method is useful during exploration/prototyping, but - because the filtering is performed client-side - this method is only really suitable for initial development/prototyping, or for classes with very few instances. Use #uniqueMatch(Query) for production code.
If no instance is found then Optional#empty() will be return, while if there is more that one instances a run-time exception will be thrown.
uniqueMatch(Query)
Find the only instance that matches the provided Query .
This is the main API for server-side (performant) queries returning no more than one instance, where a org.apache.causeway.applib.query.NamedQuery can be passed in that ultimately describes a SELECT query with WHERE predicates. The mechanism by which this is defined depends on the ORM (JDO or JPA). A org.apache.causeway.applib.query.NamedQuery can optionally specify a org.apache.causeway.applib.query.NamedQuery#withRange(QueryRange) range of instances to be returned.
If no instance is found then Optional#empty() will be return, while if there is more that one instances a run-time exception will be thrown.
firstMatch(Class, Predicate)
Find the only instance of the specified type (including subtypes) that satifies the provided (client-side) predicate.
This method is useful during exploration/prototyping, but - because the filtering is performed client-side - this method is only really suitable for initial development/prototyping, or for classes with very few instances. Use #firstMatch(Query) for production code.
If no instance is found then Optional#empty() will be return, while if there is more that one instances then the first will be returned.
firstMatch(Query)
Find the only instance that matches the provided Query , if any.
This is the main API for server-side (performant) queries returning the first matching instance, where a org.apache.causeway.applib.query.NamedQuery can be passed in that ultimately describes a SELECT query with WHERE predicates. The mechanism by which this is defined depends on the ORM (JDO or JPA). A org.apache.causeway.applib.query.NamedQuery can optionally specify a org.apache.causeway.applib.query.NamedQuery#withRange(QueryRange) range of instances to be returned.
If no instance is found then Optional#empty() will be return, while if there is more that one instances then the first will be returned.
Implementation
The Commons Persistence module provides a default implementation of this service, RepositoryServiceDefault.
Configuration Properties
The default implementation of this domain service supports the following configuration properties:
Property | Value (default value) |
Description |
---|---|---|
|
|
Whether the |
Usage
Persist
Use an n-arg constructor, eg:
Customer cust = repositoryService.detachedEntity(
new Customer("Freddie", "Mercury"));
repositoryService.persist(cust);
rather than the deprecated version that takes a type:
Customer cust = repositoryService.detachedEntity(Customer.class);
cust.setFirstName("Freddie");
cust.setLastName("Mercury");
repositoryService.persist(cust);
You should be aware that by default the framework queues up calls to #persist() and #remove(). These are then executed either when the request completes (and the transaction commits), or if the queue is flushed. This can be done either implicitly by the framework, or as the result of a direct call to TransactionService#flushTransaction.
By default the framework itself will cause #flush()
to be called whenever a query is executed by way of #allMatches(Query)
.
However, this behaviour can be disabled using the Other
persistAndFlush(…)
, removeAndFlush(…)
In some cases, such as when using managed properties and collections for implementing 1-1, 1-n, or m-n relationships, the developer needs to invoke flush()
to send the changes to the persistence mechanism.
These managed properties and collections and then updated.
The persistAndFlush(…)
and removeAndFlush(…)
methods save the developer from having to additionally call the flush(…)
method after calling persist()
or remove()
.
For example, the following code requires a flush to occur, so uses these methods:
public abstract class Warehouse extends SalesVIPEntity<Marketplace> {
@Persistent(mappedBy = "marketplace", dependentElement = "true")
@Getter @Setter
SortedSet<MarketplaceExcludedProduct> excludedProducts =
new TreeSet<MarketplaceExcludedProduct>();
@Action(semantics = SemanticsOf.IDEMPOTENT)
public MarketplaceExcludedProduct addExcludedProduct(final Product product) {
val marketplaceExcludedProduct = findExcludedProduct(product);
if (marketplaceExcludedProduct == null) {
marketplaceExcludedProduct =
repositoryService.detachedEntity(
new MarketplaceExcludedProduct.builder()
.marketPlace(this)
.product(product)
.build());
}
repositoryService.persistAndFlush(marketplaceExcludedProduct); (1)
return marketplaceExcludedProduct;
}
@Action(semantics = SemanticsOf.IDEMPOTENT)
public void removeExcludedProducts(final Product product) {
val marketplaceExcludedProduct = findExcludedProduct(product);
if (marketplaceExcludedProduct != null) {
repositoryService.removeAndFlush(marketplaceExcludedProduct);
}
}
...
}
1 | Needed for updating the managed properties and collections. |
On the “addExcludedProduct()” action, if the user didn’t flush, the following test would fail because the managed collection would not containing the given product:
@Test
public void addExcludedProduct() {
// given
final AmazonMarketplace amazonMarketplace = this.wrapSkipRules(
this.marketplaceRepository).findOrCreateAmazonMarketplace(
AmazonMarketplaceLocation.FRANCE);
final Product product = this.wrap(this.productRepository)
.createProduct(UUID.randomUUID().toString(), UUID.randomUUID().toString());
// when
this.wrap(amazonMarketplace).addExcludedProduct(product);
// then
Assertions.assertThat(
this.wrapSkipRules(amazonMarketplace).findAllProductsExcluded()
).contains(product); (1)
}
1 | this would fail. |
Named queries and xxxMatches(…)
There are two subtypes of the Query
API, namely NamedQuery
and AllInstancesQuery
.
The former is the more important, as it identifies a named query and a set of parameter/argument tuples, and is executed server-side.
For example, using JDO a ToDoItem
could be annotated:
@javax.jdo.annotations.Queries( {
@javax.jdo.annotations.Query(
name = "findByAtPathAndComplete", language = "JDOQL", (1)
value = "SELECT "
+ "FROM todoapp.dom.module.todoitem.ToDoItem "
+ "WHERE atPath.indexOf(:atPath) == 0 " (2)
+ " && complete == :complete"), (3)
// ...
})
public class ToDoItem ... {
// ...
}
1 | name of the query |
2 | defines the atPath parameter |
3 | defines the complete parameter |
This JDO query definitions are used in the ToDoItemRepositoryImplUsingJdoql
service:
import org.springframework.stereotype.Service;
@Service
public class ToDoItemRepositoryImplUsingJdoql implements ToDoItemRepositoryImpl {
@Programmatic
public List<ToDoItem> findByAtPathAndCategory(final String atPath, final Category category) {
return repositoryService.allMatches(
Query.named(ToDoItem.class, "findByAtPathAndCategory") (1)
.withParameter("atPath", atPath) (2)
.withParameter("category", category)); (3)
}
...
@javax.inject.Inject
RepositoryService repositoryService;
}
1 | corresponds to the "findByAtPathAndCategory" JDO named query |
2 | provide argument for the atPath parameter. |
3 | provide argument for the category parameter. |
If using JPA, it is also possible to use the Spring Data repositories, using JpaSupportService. |
If using JDO/DataNucleus, it is also possible to use the DataNucleus type-safe query API, see JdoSupportService. |