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 JPA an entity could be annotated:

@NamedQueries({
        @NamedQuery(
                name = "SimpleObject.findByNameLike",       (1)
                query = "SELECT so " +
                        "FROM SimpleObject so " +
                        "WHERE so.name LIKE :name"          (2)
        )
})
})
public class SimpleObject ... {
    // ...
}
1 name of the query
2 defines the name parameter

This query definition is then typically used in a corresponding repository service:

import org.apache.causeway.applib.services.repository.RepositoryService;

public List<SimpleObject> findByNameLike(
        @Name final String name) {
    return repositoryService.allMatches(
            Query.named(SimpleObject.class, "SimpleObject.findByNameLike")  (1)
                 .withParameter("name", "%" + name + "%"));                 (2)
}

@Inject RepositoryService repositoryService;                                (3)
1 name of the query
2 argument for the name parameter.
3 generic repository service, provided by Causeway

It is also possible to use the Spring Data repositories, which automatically generate an implementation based on the method signature. Or, you could use the QueryDSL integration.

See also

Supporting classes used by the API:

Transaction management: