Apache Cayenne > Index > Cayenne Examples > Spring Integration Examples
This page provides access to the code for integrating Cayenne with the Spring container:
- Spring/Cayenne Petclinic by Andrus
- Spring/Cayenne/Click Example please see the CayenneTemplate as discussed later
Note that if you are not currently using Spring, consider whether it is worth adding another layer of indirection to your application. You may discover that Cayenne already does what you expected Spring to do.
Reasons to Use Spring in Cayenne Application
- Spring provides a good business/middle tier service design pattern.
- Spring provides good support for unit testing.
- You are already using Spring for other purposes, and want it to stay the only place where configuration resides.
- In addition to ORM, there are other things that are wrapped in a single transaction.
Reasons Not to Use Spring in Cayenne Application
(or rather reasons why people use Spring with other ORMs that do not apply to Cayenne)
- Spring provide good ORM configuration support. Cayenne configuration is extremely simple. Using Spring to configure Cayenne introduces another layer of indirection with little benefit.
- Spring provides ThreadContext support for ORM packages. Cayenne already provides a ThreadContext based model in the DataContext class.
- Spring is good in providing an easy to use API wrapper around difficult to use APIs. The DataContext API is very good, so Spring provides little additional benefit.
- Spring maps checked persistence framework exceptions in RuntimeException making the API easier to program to. Cayenne uses unchecked exceptions so this isn't an issue.
- Spring can provide an open session in view filter to help deal with Hibernates lazy loading problems in web applications. Cayenne doesn't have this issue as all registered objects are "attached" and can lazy load when rendering in web pages.
- Spring provides a Transaction wrapper on top of JDBC or Hibernate calls. Cayenne transactions are already nicely encapsulated in the DataContext (but note the more complex transaction scenario in the "reasons to use" above).
Spring Cayenne Design Patterns
Spring uses an ORM Template design pattern whereby persistence frameworks are supported with a Template helper classes which makes the ORM easier to use. ORM Template classes are typically used by DAO implementation or Service implementation classes.
An example Spring CayenneTemplate is provided below.
public class CayenneTemplate { protected boolean autoRollback; protected boolean isAutoRollback() { return autoRollback; } protected void setAutoRollback(boolean autoRollback) { this.autoRollback = autoRollback; } protected DataContext getThreadDataContext() { return DataContext.getThreadDataContext(); } protected void commitChanges() { try { getThreadDataContext().commitChanges(); } catch (CayenneRuntimeException cre) { if (isAutoRollback()) { rollbackChanges(); } } } protected void rollbackChanges() { getThreadDataContext().rollbackChanges(); } protected DataObject createAndRegisterNewObject(Class objectClass) { return getThreadDataContext().createAndRegisterNewObject(objectClass); } protected void registerNewObject(DataObject dataObject) { getThreadDataContext().registerNewObject(anObject); } protected void unregisterObjects(Collection dataObjects) { getThreadDataContext().unregisterObjects(dataObjects); } protected void invalidateObjects(Collection dataObjects) { getThreadDataContext().invalidateObjects(dataObjects); } protected void deleteObject(DataObject anObject) { getThreadDataContext().deleteObject(anObject); } protected DataObject refetchObject(DataObject anObject) { getThreadDataContext().refetchObject(anObject.getObjectId()); } protected List performQuery(SelectQuery query) { return getThreadDataContext().performQuery(query); } protected List performQuery(String queryName, boolean refresh) { return getThreadDataContext().performQuery(query, refresh); } protected List performQuery(String queryName, Map parameters, boolean refresh) { return getThreadDataContext().performQuery(queryName, parameters, refresh); } protected Object objectForPK(final Class dataObjectClass, final Object pk) { return DataObjectUtils.objectForPK(getThreadDataContext(), dataObjectClass, pk); } }
An example Clinic Service/DAO implementation class which extends CayenneTemplate is provided below.
public class ClinicImpl extends CayenneTemplate implements Clinic { public Collection getVets() { return performQuery(new SelectQuery(Vet.class)); } public Collection getPetTypes() { return performQuery(new SelectQuery(PetType.class)); } public Collection findOwners(String lastName) { return performQuery("OwnerSearch", Collections.singletonMap("name", "%" + lastName + "%"), true); } public Owner loadOwner(int id) { return (Owner) objectForPK(Owner.class, new Integer(id)); } public Pet loadPet(int id) { return (Pet) objectForPK(Pet.class, new Integer(id)); } public void storeOwner(Owner owner) { if (owner.getDataContext() == null) { registerNewObject(owner); } commitChanges(); } public void storePet(Pet pet) { commitChanges(); } public void storeVisit(Visit visit) { commitChanges(); } }
To use Cayenne enabled and Spring injected unit test you could use the following abstract TestCase:
import org.objectstyle.cayenne.access.DataContext; import org.objectstyle.cayenne.conf.Configuration; import org.springframework.test.AbstractDependencyInjectionSpringContextTests; /** * Provides Cayenne <tt>AbstractDependencyInjectionSpringContextTests<tt> * for unit testing Spring components reliant on an initialize Cayenne * <tt>Configuration</tt> and a thread local <tt>DataContext</tt> */ public abstract class AbstractCayenneSpringTestCase extends AbstractDependencyInjectionSpringContextTests { /** * This method is called before a test is executed, creating a DataContext * and associating it to the current Thread.. * * @see AbstractDependencyInjectionSpringContextTests#onSetUp() */ protected void onSetUp() throws Exception { DataContext dataContext = DataContext.createDataContext(); DataContext.bindThreadDataContext(dataContext); } /** * This method is called after a test is executed, removing the DataContext * from the Thread. * * @see AbstractDependencyInjectionSpringContextTests#onTearDown() */ protected void onTearDown() throws Exception { DataContext.bindThreadDataContext(null); } }
Your Clinic service test case would extend this class:
public class ClinicTestCase extends AbstractCayenneSpringTestCase { protected String[] getConfigLocations() { return new String[] { "/applicationContext.xml" }; } protected Clinic clinic; public Clinic getClinic() { return clinic; } public void setClinic(Clinic clinic) { this.clinic = clinic; } public void test() { Collection petTypes = getClinic().getPetTypes(); assertNotNull(petTypes); assertEquals(8, petTypes.size()); .. } }