We provide a variety of services including:
· Training classes for Spring, Hibernate and Acegi Security
· Jumpstarts to get your project off to the right start
· Reviews to improve your architecture, code and development process
For more information visit our website: http://www.chrisrichardson.net
Chris Richardson, chris@chrisrichardson.net
The Repository pattern is one of the patterns described in the excellent book Domain-Driven Design by Eric Evans. A repository is a class that provides the illusion of an in-memory collection of objects and has methods for adding and removing objects and finding objects that match some criteria. The collection of objects managed by a repository almost always resides in the database. As simple as the idea sounds, it’s one of the most frequently discussed topics on the domain-driven design mailing list. In this brief article, I describe how I implement repositories.
One of the central domain classes in the Project Track application is the Project. The domain model defines a ProjectRepository class, which provides access to projects. Here is the interface:
public interface ProjectRepository {
public void add(Project project);
public Project get(int id);
public Project merge(Project project);
public List getAllProjects(ProjectColumnType sortColumn);
public List getProjectsWaitingApprovalByRole(RoleType role,
ProjectColumnType sortColumn);
}
The purpose of each method is as follows:
· add() - persists a newly created project
· get() – retrieves a project by it’s id
· merge() – reattaches object by merging
· getAllProjects() – retrieves all projects sorted in the specified way
· getProjectsWaitingApprovalByRole() – returns all projects that are waiting approval by someone of particular role
The implementation has a single implementation class, which uses the Spring/Hibernate APIs to access the database.
public class HibernateProjectRepository
extends HibernateDaoSupport implements ProjectRepository {
public void add(Project project) {
getHibernateTemplate().save(project);
}
public Project get(int id) {
Project project = (Project)
getHibernateTemplate().get(Project.class,id);
return project;
}
public Project merge(Project project) {
return (Project) getHibernateTemplate().merge(project);
}
public List getAllProjects(ProjectColumnType sortColumn) {
String queryString = "from Project as p order by p." +
computeSortOrder(sortColumn);
List projects = getHibernateTemplate().find(queryString);
return projects;
}
protected String computeSortOrder(ProjectColumnType sortColumn) {
if (sortColumn == null ||
ProjectColumnType.NAME.equals(sortColumn))
return "name";
if (ProjectColumnType.ROLE.equals(sortColumn))
return "status.role";
if (ProjectColumnType.STATUS.equals(sortColumn))
return "status.name";
if (ProjectColumnType.TYPE.equals(sortColumn))
return "type";
throw new UnsupportedOperationException("Unsupported sort order: "
+ sortColumn);
}
public List getProjectsWaitingApprovalByRole(final RoleType role,
final ProjectColumnType sortColumn) {
return getHibernateTemplate().executeFind(new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(Project.class);
criteria.createAlias("status", "status");
criteria.add(Restrictions.eq("status.role", role));
criteria.addOrder(Order.asc(computeSortOrder(sortColumn)));
return criteria.list();
}
});
}
}
This class is in a different package to the interface and in some cases in an entirely separate project.
For testing, I don’t bother with an in-memory implementation of the repository interface. I long ago decided that it was too much work. Instead, some tests use mock objects (http://projecttrack.googlecode.com/svn/trunk/services/src/test/java/org/jia/ptrack/services/ProjectCoordinatorImplMockTests.java) and others use HSQLDB, an in-memory database, with a schema that Hibernate automatically generates from the mapping file.
That’s pretty much all there is to it.