Using Cayenne

This page is the repository for information on programming with Cayenne, Apache's persistence technology.

Ian Boston has had an initial positive experience with Cayenne which he wrote up on his blog http://www.tfd.co.uk/blogs/sakaiblog/2007/04/28/apache-cayenne-is-cool-hot-so-far/ which has prompted us to investigate it.

An important issue is the status of Spring integration, on which the evidence appears a bit conflicting. The earliest part of the trail is the following JIRA on the Spring main site:

http://opensource.atlassian.com/projects/spring/browse/SPR-619

From the point of view of the Spring developers, it has been "mutually agreed" that putting Spring support within Cayenne is the best approach for all.

The following post on the Cayenne-dev mailing list suggests the enthusiasm may have been a bit less than mutual:

http://objectstyle.org/cayenne/lists/cayenne-devel/2005/05/0208.html

The main repository for Spring-Cayenne information is on the following Cayenne wiki page:

http://cwiki.apache.org/CAY/spring-integration-examples.html

On the promising side, this suggests that many of the issues plaguing Hibernate ORM have been resolved - in particular the "permanent"-seeming attachment of persistent objects sounds very intriguing and is well worth investigating. Here is the Spring Cayenne config file from Cayenne's version of the standard Spring Pet Clinic sample, which suggests all of this stuff works pretty straightforwardly:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
	"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- Application context definition for Petclinic on Cayenne -->
<beans>
	<!-- ========================= RESOURCE DEFINITIONS ========================= -->
	<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
	<!-- (in this case, JDBC-related settings for the dataSource definition below) -->
	<bean id="propertyConfigurer" 
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location">
			<value>/WEB-INF/jdbc.properties</value>
		</property>
	</bean>
	<!-- Local DataSource that works in any environment -->
	<bean id="dataSource" 
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName">
			<value>${jdbc.driverClassName}</value>
		</property>
		<property name="url">
			<value>${jdbc.url}</value>
		</property>
		<property name="username">
			<value>${jdbc.username}</value>
		</property>
		<property name="password">
			<value>${jdbc.password}</value>
		</property>
	</bean>
	<!-- JNDI DataSource for J2EE environments -->
	<!--
	<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiName"><value>java:comp/env/jdbc/petclinic</value></property>
	</bean>
	-->
	<!-- Transaction manager for a single JDBC DataSource (alternative to JTA) -->
	<bean id="transactionManager" 
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource">
			<ref local="dataSource"/>
		</property>
	</bean>
	<!-- =============== CAYENNE STACK AND INTERCEPTOR DEFINITIONS ========================= -->
	<bean id="cayenneWebInterceptor" 
		class="org.springframework.orm.cayenne.WebInterceptor">
		<!-- configuring to use default domain from configuration -->
		<constructor-arg>
			<ref local="cayenneConfig"/>
		</constructor-arg>
	</bean>
	<!-- Load Cayenne with default location of cayenne.xml and Cayenne-configured DataSource -->
	<bean id="cayenneConfig" 
		class="org.springframework.orm.cayenne.ConfigurationFactory">
		<property name="dataSource">
			<ref local="dataSource"/>
		</property>
	</bean>
	<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->
	<!--
	   Petclinic's central data access object, Cayenne implementation.
       -->
	<bean id="clinicTarget" 
		class="org.springframework.samples.petclinic.cayenne.CayenneClinic">
		<property name="autoRollback">
			<value>true</value>
		</property>
	</bean>
	<!--
		- Transactional proxy for Petclinic's central data access object.
		- Note that in a real-life app with multiple transaction proxies,
		- you will probably want to use parent and child bean definitions
		- as described in the manual, to reduce duplication.
       -->
	<bean id="clinic" 
		class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="transactionManager">
			<ref local="transactionManager"/>
		</property>
		<property name="target">
			<ref local="clinicTarget"/>
		</property>
		<property name="transactionAttributes">
			<props>
				<prop key="*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>
</beans>

To me the main question mark over Cayenne is the standard question mark over all "slick" ORM solutions re their handling of "high update" situations. Our real show-stopper with Hibernate as discovered by Ian and others is the possibility of very frequent deadlocks when dealing with high-load, high-update applications under MySQL. Cayenne looks like it could be a better choice than Hibernate over the domain for which it is already recommend in Sakai however (that is, low-to-moderate complexity, low-update applications).

More problematic with Cayenne is its general intrusiveness. Cayenne does NOT work with plain POJOs but requires at the very least an interface (Persistent) and in practice also a base class (DataObject) for all entities in its model. Since the data model probably autogenerated this may not be such a serious issue but DOES essentially tie one to a dual "wrappers + impl" approach. Cayenne is "planned" to move along to a more modern POJO + CGLib-based solution, but as of 3.0 (still far from release) this is still only in planning. The Cayenne "DataContext" fulfils pretty much exactly the role of Hibernate Session, and so the advantages from Cayenne might more strongly arise from the toolset and Eclipse plugins, as well as the generally slightly increased transparency of the codebase.