This page highlights the new features and changes introduced in 3.1 release. It is a high-level overview and as such is by no means complete. Consult RELEASE-NOTES.txt file included in each release for the full list of changes, and UPGRADE.txt for the upgrade instructions.
Cayenne distribution is made leaner and more modular:
![]() | The new DI-based bootstrap and configuration approach is not API-compatible with earlier versions of Cayenne. Make sure you read the UPGRADE.txt file for instructions how to upgrade the existing projects. |
Cayenne 3.1 runtime stack is built around the ideas of Dependency Injection (DI), making it extremely flexible and easy to extend. It bundles a small, flexible annotations-based DI container to configure its services. The container provides DI services and exposes Cayenne extension points, but does not interfere with other DI containers that may be present in the application. I.e. it is invisible to the users who do not care about advanced Cayenne customization.
TODO... For now read the UPGRADE.txt file and check the tutorials source code for bootstrap examples. Here is a simple example of starting a server-side Cayenne stack:
ServerRuntime runtime = new ServerRuntime("cayenne-UntitledDomain.xml");
Cayenne 3.1 provides a property-based mechanism to override Modeler DataSource definitions, regardless of whether they are driver configurations, JNDI, DBCP, etc. A quick configuration example is shown below:
-Dcayenne.jdbc.driver=com.mysql.jdbc.Driver -Dcayenne.jdbc.url=jdbc:mysql://localhost/mydb \ -Dcayenne.jdbc.username=user -Dcayenne.jdbc.password=password
For more details and configuration options see javadocs of org.apache.cayenne.configuration.server.PropertyDataSourceFactory.
This feature supersedes what was formerly known as "JNDI hack", i.e. JNDI DataSource failover load strategy based on CayenneModeler preferences database. The problem with JNDI hack was unstable and frequently corrupted preferences database, and the need to include hsqldb and cayenne-modeler jars in the runtime.
See UPGRADE.txt for the full list of changes
Cayenne 3.1 features support for annotations on lifecycle listeners (but not yet on entity callback methods) that simplifies registering listeners via API. Our experience with Cayenne 3.0 shows that mapping listeners in the Modeler doesn't scale well to complex applications, and 3.0 API for mapping the listeners is hard to use. In 3.1 you can annotate listener methods and register multiple callback methods with a single call.
// declare a listener with annotated methods class MyListener { @PostLoad(Entity1.class) @PostPersist(Entity1.class) void postLoad(Object object) { .... } } // register a listener ServerRuntime runtime = .. MyListener listener = new MyListener(); runtime.getChannel().getEntityResolver().getCallbackRegistry().addListener(listener);
Moreover, unlike JPA annotations, Cayenne allows to attach a listener to a set of entities not known to the listener upfront, but that are all annotated with some custom annotation:
class MyListener {
@PostLoad(entityAnnotations = CustomAnnotation.class)
void postLoad(Object object) {
....
}
}
Cayenne now features a DataChannelFilter interface that allows to intercept and alter all DataChannel traffic (i.e. selects and commits between a DataContext and DataDomain). It provides a chain of command API very similar to servlet filters. Filters are widely used by "cayenne-lifecyle" extensions and allow to build powerful custom object lifecycle-aware code. To install a filter, the following API is used:
class MyFilter implement DataChannelFilter { .. }
MyFilter filter = new MyFilter();
ServerRuntime runtime = ..
runtime.getDataDomain().addFilter(filter);
Very often filters mark some of their own methods with lifecycle annotations so that certain operations can be triggered by Cayenne inside the scope of filter's onQuery() or onSync() methods. To ensure annotated methods are invoked, filter registration should be combined with listener registration:
MyFilter filter = new MyFilter(); ServerRuntime runtime = .. runtime.getDataDomain().addFilter(filter); runtime.getDataDomain().getEntityResolver().getCallbackRegistry().addListener(filter); // noticed that by default runtime.getDataDomain() is equivalent to runtime.getChannel()
We got rid of HSQLDB-based preferences storage, and are using standard Java Preferences API for the Modeler preferences. This solved a long-standing stability issue with Modeler preferences. So no more lost user preferences.
Cayenne 3.1 includes an optional cayenne-lifecyle module that implements a few useful extensions based on DataChannelFilters and lifecycle annotations. Those include a concept of UUID (which is a String URL-friendly representation of ObjectId), support for (de)referencing objects by UUID, UUID-based relationships, annotation-based cache groups invalidation, annotation-based audit of object changes, etc.