Examples and Usage

The JdoSupportService service provides a number of general purpose methods for working with the JDO/DataNucleus objectstore. In general these act at a lower-level of abstraction than the APIs normally used (specifically, those of RepositoryService), but nevertheless deal with some of the most common use cases. For service also provides access to the underlying JDO PersistenceManager for full control.

The following sections discuss the functionality provided by the service, broken out into categories.

Executing SQL

You can use the JdoSupportService to perform arbitrary SQL SELECTs or UPDATEs:

public interface JdoSupportService {
    @Programmatic
    List<Map<String, Object>> executeSql(String sql);
    @Programmatic
    Integer executeUpdate(String sql);
    ...
}

The executeSql(…​) method allows arbitrary SQL SELECT queries to be submitted:

List<Map<String, Object>> results = jdoSupportService.executeSql("select * from custMgmt.customers");

The result set is automatically converted into a list of maps, where the map key is the column name.

In a similar manner, the executeUpdate(…​) allows arbitrary SQL UPDATEs to be performed.

int count = jdoSupportService.executeUpdate("select count(*) from custMgmt.customers);

The returned value is the number of rows updated.

As an alternative, consider using DataNucleus' type-safe JDO query API, discussed below.

Type-safe JDOQL Queries

DataNucleus provides an extension to JDO, so that JDOQL queries can be built up and executed using a set of type-safe classes.

The types in question for type safe queries are not the domain entities, but rather are companion "Q…​" query classes. These classes are generated dynamically by an annotation processor as a side-effect of compilation, one "Q…​" class for each of the @PersistenceCapable domain entity in your application. For example, a ToDoItem domain entity will give rise to a QToDoItem query class. These "Q…​" classes mirror the structure of domain entity, but expose properties that allow predicates to be built up for querying instances, as well as other functions in support of order by, group by and other clauses.

The IntelliJ IDE automatically enables annotation processing by default, as does Maven. Using Eclipse IDE you may need to configure annotation processing manually; see the Setup Guide. The DataNucleus' documentation offers some guidance on confirming that APT is enabled.

The JdoSupportService service offers two methods at different levels of abstraction:

public interface JdoSupportService {
    <T> List<T> executeQuery(final Class<T> cls, final BooleanExpression be);
    <T> T executeQueryUnique(final Class<T> cls, final BooleanExpression be);
    <T> TypesafeQuery<T> newTypesafeQuery(Class<T> cls);
    ...
}

The executeQuery(…​) method supports the common case of obtaining a set of objects that meet some criteria, filtered using the provided BooleanExpression. To avoid memory leaks, the returned list is cloned and the underlying query closed.

For example, consider an implementation of ToDoItemRepository using type-safe queries. The following JDOQL:

SELECT
FROM todoapp.dom.module.todoitem.ToDoItem
WHERE atPath.indexOf(:atPath) == 0
   && complete == :complete")

can be expressed using type-safe queries as follows:

public List<ToDoItem> findByAtPathAndCategory(final String atPath, final Category category) {
    final QToDoItem q = QToDoItem.candidate();
    return jdoSupportService.executeQuery(ToDoItem.class,
            q.atPath.eq(atPath).and(
            q.category.eq(category)));
}

You can find the full example of the JDOQL equivalent in the RepositoryService

The executeUniqueQuery(…​) method is similar to executeQuery(…​), however expects the query to return at most a single object, which it returns (or null if none).

The newTypesafeQuery(…​) method is a lower-level API that allows a type safe query to be instantiated for most sophisticated querying, eg using group by or order by clauses. See the DataNucleus documentation for full details of using this.

One thing to be aware of is that after the query has been executed, it should be closed, using query.closeAll(). If calling query.executeList() we also recommend cloning the resultant list first. The following utility method does both of these tasks:

private static <T> List<T> executeListAndClose(final TypesafeQuery<T> query) {
    final List<T> elements = query.executeList();
    final List<T> list = Lists.newArrayList(elements);
    query.closeAll();
    return list;
}

Fixture support

When writing integration tests you’ll usually need to tear down some/all mutable transactional data before each test. One way to do that is to use the executeUpdate(…​) method described above.

Alternatively, the deleteAll(…​) method will let your test delete all instances of a class without resorting to SQL:

public interface JdoSupportService {
    void deleteAll(Class<?>... pcClasses);
    ...
}

For example:

public class TearDownAll extends FixtureScriptAbstract {
    @Override
    protected void execute(final ExecutionContext ec) {
        jdoSupportService.deleteAll(Order.class);
        jdoSupportService.deleteAll(CustomerAddress.class);
        jdoSupportService.deleteAll(Customer.class);
    }
    @Inject
    JdoSupportService jdoSupportService;
}

It can occasionally be the case that Apache Causeway' internal adapter for the domain object is still in memory. JDO/DataNucleus seems to bump up the version of the object prior to its deletion, which under normal circumstances would cause Apache Causeway to throw a concurrency exception. Therefore to prevent this from happening (ie to force the deletion of all instances), concurrency checking is temporarily disabled while this method is performed.

Reloading entities

An (intentional) limitation of JDO/DataNucleus is that persisting a child entity (in a 1:n bidirectional relationship) does not cause the parent’s collection to be updated.

public interface JdoSupportService {
    <T> T refresh(T domainObject);
    void ensureLoaded(Collection<?> collectionOfDomainObjects);
    ...
}

The refresh(T domainObject) method can be used to reload the parent object (or indeed any object). Under the covers it uses the JDO PersistenceManager#refresh(…​) API.

The ensureLoaded(…​) method allows a collection of domain objects to be loaded from the database in a single hit. This can be valuable as a performance optimization to avoid multiple roundtrips to the database. Under the covers it uses the PersistenceManager#retrieveAll(…​) API.

JDO PersistenceManager

The functionality provided by JdoSupportService focus only on the most common use cases. If you require more flexibility than this, eg for dynamically constructed queries, then you can use the service to access the underlying JDO PersistenceManager API:

public interface JdoSupportService {
    PersistenceManager getJdoPersistenceManager();
    ...
}

For example:

public class Orders {

    public List<Order> findOrders( /* ... */ ) {
        javax.jdo.PersistenceManager pm = jdoSupportService.getPersistenceManager();

        ...

        return someListOfOrders;
    }

    @Inject
    JdoSupportService jdoSupportService;
}