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

RepositoryService.java
interface RepositoryService {
  EntityState getEntityState(Object object)     (1)
  T detachedEntity(T entity)     (2)
  T persist(T domainObject)     (3)
  T persistAndFlush(T domainObject)     (4)
  void persistAndFlush(Object... domainObjects)     (5)
  void remove(Object domainObject)     (6)
  void removeAndFlush(Object domainObject)     (7)
  void removeAll(Class<T> cls)     (8)
  List<T> allInstances(Class<T> ofType)     (9)
  List<T> allInstances(Class<T> ofType, long start, long count)     (10)
  List<T> allMatches(Class<T> ofType, Predicate<? super T> predicate)     (11)
  List<T> allMatches(Class<T> ofType, Predicate<? super T> predicate, long start, long count)     (12)
  List<T> allMatches(Query<T> query)     (13)
  Optional<T> uniqueMatch(Class<T> ofType, Predicate<T> predicate)     (14)
  Optional<T> uniqueMatch(Query<T> query)     (15)
  Optional<T> firstMatch(Class<T> ofType, Predicate<T> predicate)     (16)
  Optional<T> firstMatch(Query<T> query)     (17)
  T refresh(T pojo)     (18)
  T detach(T entity)     (19)
}
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 persist(T)

Persist the specified object (or do nothing if already persistent).

4 persistAndFlush(T)

Persist the specified object (or do nothing if already persistent) and flushes changes to the database.

5 persistAndFlush(Object)

Persist the specified objects (or do nothing if already persistent) and flushes changes to the database.

6 remove(Object)

Remove (ie delete) an object from the persistent object store (or do nothing if it has already been deleted).

7 removeAndFlush(Object)

Deletes the domain object but only if is persistent, and flushes changes to the database (meaning the object is deleted immediately).

8 removeAll(Class)

Removes all instances of the domain object.

9 allInstances(Class)

Returns all persisted instances of specified type (including subtypes).

10 allInstances(Class, long, long)

Overload of #allInstances(Class) , returns a page of persisted instances of specified type (including subtypes).

11 allMatches(Class, Predicate)

Returns all the instances of the specified type (including subtypes) that the predicate object accepts.

12 allMatches(Class, Predicate, long, long)

Overload of #allMatches(Class, Predicate) , returns a page of persisted instances of specified type (including subtypes).

13 allMatches(Query)

Returns all the instances that match the given Query .

14 uniqueMatch(Class, Predicate)

Finds the only instance of the specified type (including subtypes) that satifies the (client-side) predicate.

15 uniqueMatch(Query)

Find the only instance that matches the provided Query .

16 firstMatch(Class, Predicate)

Find the only instance of the specified type (including subtypes) that satifies the provided (client-side) predicate.

17 firstMatch(Query)

Find the only instance that matches the provided Query , if any.

18 refresh(T)

Reloads the domain entity from the database.

19 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.

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.

refresh(T)

Reloads the domain entity from the database.

detach(T)

Explicitly detaches the entity from the current persistence session.

This allows the entity to be read from even after the PersistenceSession that obtained it has been closed.

Implementation

The default implementation of this domain service is o.a.c.core.runtimeservices.repository.RepositoryServiceDefault.

Configuration Properties

The default implementation of this domain service supports the following configuration properties:

Property Value
(default value)
Description

causeway.core.runtime-services.
repository-service.
disable-auto-flush

true,false
(false)

Whether the RepositoryService should automatically flush pending changes prior to querying (via allMatches(), firstMatch() and so on).

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)
                        "atPath", atPath,          (2)
                        "category", category));    (3)
    }
    ...
    @javax.inject.Inject
    RepositoryService repositoryService;
}
1 corresponds to the "findByAtPathAndCategory" JDO named query
2 provide argument for the atPath parameter. The pattern is parameter, argument, parameter, argument, …​ and so on.
3 provide argument for the category parameter. The pattern is parameter, argument, parameter, argument, …​ and so on.

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.

See also

Supporting classes used by the API:

Transaction management: