Chapter 5. Implementing Repositories

Table of Contents

5.1. Background
5.2. Annotating classes with @NamedQuery
5.3. Repository Implementation
5.4. Registering Repositories
5.5. Restrictions

Repositories are used to obtain references to (persisted) entities. The implementation of these repositories depends on the object store in use; those for the in-memory objectstore won't be suitable for use with JPA Objects because they naively iterate over all instances. You'll therefore need to re-implement your repositories. And for this reason it's best practice to define an interface for your repositories (or indeed any domain service). You can then switch in different implementations just by editing nakedobjects.properties (see Section 7.1, “Configure Naked Objects”).

It's good practice to put your JPA implementations into a separate Maven module. That way, you can isolate the dependencies on JPA Objects itself just to the code that needs it. The Maven archetype that comes with JPA Objects is designed to work this way; see Appendix A, Using the Maven Archetype for more details.

To understand what goes into the JPA Objects repository implementations, let's start with a little background.

5.1. Background

Naked Objects provides generic repository support through its convenience adapter classes in the Naked Objects applib. Although not mandatory, it's easiest to have domain objects inherit from org.nakedobjects.applib.AbstractDomainObject and repositories - while prototyping at least - inherit from org.nakedobjects.applib.AbstractFactoryAndRepository. These both inherit from org.nakedobjects.applib.AbstractContainedObject, which in turn provides a Container property to allow the DomainObjectContainer to be injected. The DomainObjectContainer in effect is the generic repository.

Also in AbstractContainedObject are some convenience methods for searching, each of which just delegates to similarly named methods in DomainObjectContainer. First and most straightforwardly we can request all instances of a given entity class:

  • allInstances(Class<T> ofClass): List<T>

But it is also possible to request all instances matching either a condition. This can be expressed in one of four ways:

  • allMatches(Class<T> ofClass, String title): List<T>

    With this method we search by title. This makes sense for those classes where the title is relatively small and known to be unique. However, Naked Objects does not itself mandate unique titles; they are just labels that are unique "enough" for the objects being viewed by the end-user. This is also not a good option if the title changes, eg reflecting the state of the object

  • allMatches(Class<T> ofClass, T pattern): List<T>

    Here we search using a pattern object, sometimes called query-by-example. Only instances whose values match those of the (set) properties of the pattern instance are returned.

  • allMatches(Class<T> ofClass, Filter<T> filter): List<T>

    The Filter<T> interface (defined in the Naked Objects applib) acts as a predicate, so the method returns only those instances that meet the filter.

  • allMatches(Class<T> ofClass, Query<T> query): List<T>

    This method is similar to the one for filtering, returning those instances that meet the query specification. (Again, Query<T> is defined in the Naked Objects applib).

There are similar methods to find the first instance:

  • firstMatch(Class<T> ofClass, String title): T

  • firstMatch(Class<T> ofClass, T pattern): T

  • firstMatch(Class<T> ofClass, Filter<T> filter): T

  • firstMatch(Class<T> ofClass, Query<T> query): T

There are also methods to find the first and only (unique) instance:

  • uniqueMatch(Class<T> ofClass, String title): T

  • uniqueMatch(Class<T> ofClass, T pattern): T

  • uniqueMatch(Class<T> ofClass, Filter<T> filter): T

  • uniqueMatch(Class<T> ofClass, Query<T> query): T

The difference between Filter<T> and Query<T> in these methods comes down to where the predicate is evaluated. With Filter<T>, the evaluation is in Java. What that means is that all instances are returned from the object store. In contrast Query<T> the evaluation is performed by the object store implementation. For JPA Objects, it ultimately corresponds to the "WHERE" clause in a SQL SELECT statement.

For prototyping you'll find that the first three of these are supported by both the in-memory object store and also by the XML object store. Indeed, every object store is likely to support these, because all they simply require that the object store can return all instances of a class. However, the version accepting Query<T> is different; because the Query<T> is evaluated in the object store, its implementation will in general be specific to the object store.

That said, there is in fact a default implementation of Query<T>, namely QueryDefault<T> (in the Naked Objects applib, again). This implementation simply holds onto a query name and a set of parameter/argument pairs.

The in-memory object store and XML object store do not support Query<T> in any way. JPA Objects does support Query<T>, through QueryDefault<T>. And this is what we use in our repository implementations.