Monday, September 25, 2006

Rolling with Spring and Hibernate, part 6: summary and reflections

This series of blog entries was meant to compare speed and ease of development of the Spring 2.0 and Hibernate/JPA combination with Ruby on Rails, and also to take a closer look at the new Rich Domain Model support in Spring 2.0. I am by no means an expert on RoR, I've mostly tinkered with a few toy projects to get a basic hang of it. My experience with Spring and Hibernate is a lot more extensive, going back to a few months before Spring 1.0 was released in March of 2004.

Obviously it's a lot more work to get started with this stack than RoR, but that can be vastly improved with a good prototype project. There are a number of options, ranging from a slightly expanded Maven standard archetype to Appfuse. Usually you can modify an existing project, maybe maintain a company-wide standard application - or even use spring-cookbook :-). In either case, it's not a big deal in a real world situation, since the time spent in this phase is a tiny, tiny part of the whole project. It does matter for newbies, though.

What's more important and interesting is the time and effort needed to make a change to the application. You often see the argument that RoR is more productive because you need to write less code to accomplish the same feat. Case in point: the infamous Hello World application...Ruby first:

puts "Hello, World!"

And the same thing in Java:

public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}

Clearly a big advantage for Ruby, wouldn't you say? ;-)

Jokes aside, code-completion compensates for more verbose syntax IMHO. Actually writing code isn't really time-consuming, but the number of places where you have to edit code for a particular change in the application to happen, does matter. It matters a lot. Suppose you have a Car in your domain model, and you want to add a color property. In RoR, you add the column "color" to the table "cars" in the database. That's it. There's no updating the ORM mapping files, no adding of a member , getter and setter for color in the Car class, no updating of DTOs or form beans, no extra handling of parameters. Unless you're using view scaffolding you will need to modify the views, however. This is a great boost to productivity, read- and maintainability of the application.

When working with Hibernate/JPA annotations, you get fairly close to this behaviour . Hibernate can auto-update the schema on changes to the Java model, and you only need to set the @Entity annotation on the class level for properties to be persistent by default. And there's no need for maintaining separate DTOs, as when working with EJB 2.x. If you're using the Spring MVC framework, you can even use arbitrary classes as form beans, and the domain model is of course a great candidate for that. So we can honestly say that we have a fairly competitive setup at hand.

A nice thing in Rails is the automatic conversion from id parameter to a related persisted entity, for example the category of a recipie in this particular application. The HTML form for editing a Recipe has a drop-down list where the parameter value is the id of the Category. Using Spring MVC, we need to write a (short) PropertyEditor to load and join the Recipie and the Category.

When it comes to URL mapping and controllers, Spring 2.0 brings a fantastic new addition to the table: ControllerClassNameHandlerMapping. Together with the MultiactionController we can work similarly to Rails: Any request that matches /car/* is handled by a CarController, and specifically by the method that has the same name as the end part of the url - /car/edit is handled by the edit() method, and so on. It works for other controller types as well, so you can have a SimpleFormController named EditCarController that's mapped to /editCar.

The edit-save-reload-cycle in the web server is almost zero in RoR, since Ruby is a interpreted language. Traditionally, this cycle has been very slow in Enterprise Java development, and even if you work extensively with out-of-container testing, you will need to see and test the actual application from time to time. Until we have on-the-fly class reloading in the JVM, we're going to have to reload the application after making changes to Java code. We can ease the pain by using a smart directory structure (as in this application) and a fast, handy servlet container that scans for changes and triggers reloads automatically. Another thing that's new in Spring 2.0 is the support for beans defined in script languages such as BeanShell, Groovy or even Ruby. You could for example write the controllers in Groovy and keep the service layer and everything below in Java. The possibilites are endless, and the big advantage of Spring is that you can gradually migrate to a simpler and more modern architecture while preserving and interacting with legacy components.

Working with a Rich Domain Model and IoC using the new @Configurable technique, is quite nice. It does require a few tricks and redundant configuration though. Passing the -javaagent parameter to the JVM requires setting shell variables or editing the server startup script, which is acceptable but easy to forget.

It's also unfortunate that there's no domain-specific shorthand XML configuration for the AspectJ transaction aspect, like <aop:aspectj-configured/>.

In general, it would be nice if you didn't have to specify what classes should be persisted twice, first with the @Entity annotation and the again the in SessionFactory configuration. Same thing with classes that need to be weaved with the IoC-aspect: first with @Configurable, and again in classpath:META-INF/aop.xml. With no detailed technical insight in the matter, here's how I'd like to write my configuration, using the same recursive package syntax as AspectJ:

<hibernate3:sessionFactory mappedClasses="se.petrix.cookbook.model..*"/>

<aop:aspectj-configured configuredClasses="se.petrix.cookbook.model..*"/>

<aop:aspectj-transactions/>

Better get to work on that patch then :-)

There is a lot of room for configuration improvment everywhere, now that the domain-specific XML framework is in place. My guess is that we'll see a lot of new XSDs during the 2.x series.

Mock testing is a little bit more difficult, but still manageable. If you're wiring a DAO or service layer into the model, you won't have to deal with chained interface calls either, such as sessionFactory.getCurrentSession().getCriteria(clazz).

How to write the CRUD methods is another difficult question. At first thought, the load operation isn't tied to a particular instance of an entity, since we don't have one yet. Therefore it should be static:

public static Recipe load(Long id) { ... }

Recipe r = Recipe.load(1L);

But there are two problems with this implementation: first, you don't want to make the persistence collaborator (the SessionFactory in this application, maybe a DAO layer or a DataSource in others) a static member. So, we have to wrap the static call with an instantiation:

public static Recipe load(Long id) {
return new Recipe().doLoad(id);
}

And secondly, you don't want to implement this method in every persistent domain class, instead you move it to the BasicEntity class. But how do we instantiate the parameterized class in a static context? We want something like this (in BasicEntity):
   
public static T load(Long id) {
return T.class.newInstance().doLoad(id);
}

Of course, that's not possible (T.class is illegal). I haven't found any way around this.

The choice, as I see it, is between wrapping load and other static methods such as finders in every persistent class, or completely skip static methods and instead choose one of the following styles:

// 1:
Category c = new Category().load(1L);
// 2:
Category c = new Category(1L).load();
// 3:
Category c = new Category(); c.setId(1L); c.load();
// 4: (perform a load operation in the constructor)
Category c = new Category(1L);

If anyone has opinions on this matter, please post a comment. I'm not convinced about the superiority of any of these solutions, but I've chosen the simplest one, number 3, in this application.

Rolling with Spring and Hibernate, part 5: testing

Testing a Spring/Hibernate/AspectJ rich domain application is not completely trivial. Normally, when working with the traditional three-tier architecture (DAO, service, controller), you utilize Inversion of Control instead of instatiating collaborators to be able to switch to mock implementations in isolated tests. When you're working with a rich domain model like this, the whole point is to place more logic inside the domain objects. They must in turn be provided with the necessary services through, in this case, aspect-driven IoC since it's impossible to avoid writing code that instantiates domain objects.

This means that if we want to test code that does instantiate a domain object, we can't inject a mock domain object - we have to inject the mock services into the domain object instance(s) created by the code we're testing.

In this application, there are only two "layers": the (rich) domain model and the controllers. Testing the model is simple, it's just like any normal DAO layer: we set up a test database and use the class with the world's longest name to test CRUD: AbstractTransactionalDataSourceSpringContextTests. It provides a few very useful things: the ability to run each test in a transaction that is rolled back, eliminating the need for manually cleaning the test database, IoC for wiring the tests themselves, and a JdbcTemplate for verifying the database operations and mappings. It's often a good idea to create a base class that loads test data and some other stuff that is common to all CRUD tests. Here's what I'm using:

public abstract class CookbookModelTest extends AbstractTransactionalDataSourceSpringContextTests {

SessionFactory sessionFactory;

public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}

@Override
protected String[] getConfigLocations() {
return new String[] {
"applicationContext-hibernate.xml",
"applicationContext-test.xml"
};
}

@Override
protected void onSetUpInTransaction() throws Exception {
super.onSetUpInTransaction();
jdbcTemplate.execute(
"INSERT INTO category (id, name) " +
"VALUES (10, 'Category 1')");
jdbcTemplate.execute(
"INSERT INTO category (id, name) " +
"VALUES (20, 'Category 2')");
jdbcTemplate.execute(
"INSERT INTO recipe (id, title, instructions, date, category_id) " +
"VALUES (1, 'Recipe 1', 'Test this recipe', '2006-09-10', 10)");
jdbcTemplate.execute(
"INSERT INTO recipe (id, title, instructions, date, category_id) " +
"VALUES (2, 'Recipe 2', 'Test this recipe too', '2006-09-11', 10)");
jdbcTemplate.execute(
"INSERT INTO recipe (id, title, instructions, date, category_id) " +
"VALUES (3, 'Recipe 3', 'Test this recipe three', '2006-09-12', 20)");
}

}

The session factory is useful for forcing a flush of the SQL commands to the database, for example when doing a delete, like we're doing here in the RecipeTest (inherits the CookbookModelTest above):

public void testDelete() throws Exception {
Recipe recipe = new Recipe();
recipe.setId(1L);
recipe.delete();
sessionFactory.getCurrentSession().flush();
assertEquals(0, jdbcTemplate.queryForInt("SELECT count(*) FROM Recipe WHERE id = 1"));
}

This illustrates the idea of using the JdbcTemplate to by-pass the ORM to verifty that the operation really resulted in the database changes we wanted.

Ok, that's the easy part. Now we move on to test the controller, which in some cases instantiates domain objects beyond our control. The fact that we're using the Hibernate API directly, and also the Criteria API, will make it a bit harder to set up mock expectations than if we had a plain DAO layer. Remeber how we build the Criteria queries:

@Transient
protected Session getSession() {
return sessionFactory.getCurrentSession();
}

@Transient
protected Criteria getCriteria() {
return getSession().createCriteria(this.getClass());
}

We have to mock both the SessionFactory, the Session and the Criteria...and make the mocks return other mocks! For example, we need to make the SessionFactory mock expect a call to getCurrentSession(), and return the Session mock (that in turn will expect other calls):

protected void expectGetCurrentSessionCall() {
sessionFactory.expects(once()).method("getCurrentSession")
.withNoArguments().will(new CustomStub("Session") {
public Object invoke(Invocation invocation) throws Throwable {
return (Session) session.proxy();
}
});
}

There's a similar method to set up an expectation to Session.getCriteria(clazz) in CookbookControllerTest.

In addition to this, we need an application context for weaving the domain objects according to the @Configurable annotation:

applicationContext = new StaticApplicationContext();
// It's a bit tricky to register a factory bean with a custom method name...
BeanDefinition aspectConfigurerDefinition = BeanDefinitionBuilder.
rootBeanDefinition(AnnotationBeanConfigurerAspect.class).
setFactoryMethod("aspectOf").getBeanDefinition();
// Register the configuring aspect
applicationContext.registerBeanDefinition(
AnnotationBeanConfigurerAspect.class.getName(), aspectConfigurerDefinition);
// Register the mocked session factory
applicationContext.getBeanFactory().
registerSingleton("sessionFactory",sessionFactory.proxy());
// Don't forget to refresh!
applicationContext.refresh();

This is a small application context that registers configuration aspect and the mock session factory, so that AspectJ can weave domain model classes according to classpath:META-INF/aop.xml, and inject the mock session factory on instatiation. With this in place in a controller test superclass, we can test a method that instantiates a domain object, such as this:

public ModelAndView create(HttpServletRequest request, HttpServletResponse response) {
List categories = new Category().findAll();
return new ModelAndView("recipe/edit", "categories", categories);
}

with the following code:

public void testCreate() throws Exception {
// This is a utility method to prepare mock request/response
prepareRequest("/recipie/create.htm", "GET");
expectGetCriteriaCall(Category.class);
List toReturn = new ArrayList();
toReturn.add(new Category());
toReturn.add(new Category());
criteria.expects(once()).
method("list").
withNoArguments().
will(returnValue(toReturn));

ModelAndView mav = controller.handleRequest(request, response);

assertEquals("recipie/edit", mav.getViewName());
List result = (List) mav.getModel().get("categories");
assertEquals(2, result.size());
}

Traditionally, we would mock CategoryService, and expect a call to listAllCategories(). No application context would be needed.

For those of you that feel that calls such as

new Category().findAll()

is hideous, I can only say that I agree, and we'll get to that in the final part.

Friday, September 15, 2006

Rolling with Spring and Hibernate, part 4: adding a relationship

We now have a basic application in place, that we can use to list, create, edit and delete recipes. The next step is to add the Category to our domain model, and create the relationship between Recipe and Category: a category contains many recipes, and a recipe belongs to one category.

The first step will be to create the Java class, which will inherit from BasicEntity. That way, we already have the id property and CRUD operations in place, and we only need to add one property (name) and the relation to Recipie. But we're going to start with just the name property. Here's the entire Category class:

@Entity
@Configurable(autowire=Autowire.BY_NAME)
public class Category extends BasicEntity<Category> {

String name;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

That's the model, now we need the view and the controller. Controller first:

public class CategoryController extends MultiActionController {

public ModelAndView list(HttpServletRequest request, HttpServletResponse response) throws Exception {
List<Category> categories = new Category().findAll();
return new ModelAndView("category/list", "categories", categories);
}
...
// + create, edit and delete
}

The views are very similar to the Recipie, we won't go into detail right now.

That's all you need to do in order to add a new model object and controller the project! On deploy, Hibernate will update the schema to include the new @Entity, and the controller mapping is implicitly derived from the class name CategoryController. Check out http://localhost:8080/cookbook/category/list.htm.

At this point we're almost at par with Rails productivity. We don't have scaffolding, but the code generated by "scripts/generate Category scaffold" is more or less the same as what we've just written, and we edit the Java model class and get the table generated, instead of the other way around. Generated code still needs to be tested, understood and maintained, and code completion makes writing actual code easier in a statically typed language. And in a final product, you will still need to re-write all the scaffolded views, so sooner or later Rails will lose that particular advantage.

The next step is to implement the relation between Recipe and Category, and that's an area where Rails will come out on top. First, the model: Recipe belongs to one Category:
@ManyToOne
@JoinColumn
public Category getCategory() {
return category;
}

And Category has many recipes:

@OneToMany(mappedBy="category")
public Set getRecipes() {
return recipes;
}

This is the inverse side of the relationship, which means that changes to the relationships are persisted when the Recipe is persisted. The default behaviour of Hibernate with the above annotations is to add a column "category_id" to the Recipie table, which holds the id of the owning Category and has a foreign key constraint. All of this is of course handled by Hibernate, since we're using automatic schema updating.

However, in order to bind a Recipe to a Category, we're going to have to write a custom PropertyEditor that converts the incoming parameter "1" to the Category entity with id == 1. The recipe/edit.ftl view is modified to include the following selector:

<h4>Category</h4>
<select name="category">
<#list categories as c>
<option value="${c.id}"
<#if (recipe.category)?exists && (recipe.category.id == c.id)>selected="selected"</#if>>
${c.name}
</option>
</#list>
</select>

We also add all categories to the model, in RecipeController:

List<Category> categories = new Category().findAll();
...
model.put("categories", categories);

And here's the custom binder we need:

public void setAsText(String text) throws IllegalArgumentException {
Long id = Long.valueOf(text);
Category category = new Category();
category.setId(id);
setValue(category.load());
}

This binder needs to be registered in the RecipeController:

protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
throws Exception {
binder.registerCustomEditor(Category.class, new CategoryPropertyEditor());
}

This means that for all properties of class Category, use this binder to convert the incoming request parameter string to an object of the Category type. With this binder in place, we can keep using this form of handler method for storing a Recipe:

public ModelAndView store(HttpServletRequest request, HttpServletResponse response, Recipie recipie)
throws Exception {
if (recipie.getDate() == null) {
recipie.setDate(Calendar.getInstance());
}
recipie.store();
return new ModelAndView("redirect:list.htm");
}

The request parameters are automatically bound to a Recipe object, and we simply pass it to the
handler method and store it.

An clear advantage of Rails is that relationship binding is provided out of the box, whereas in Spring MVC we need to build a custom PorpertyEditor for that. Simple properties however, are automatically bound to the Recipie object.

This concludes the application part, a CRUD interface for recipies and categories. In part 5, we move on to testing.

Tuesday, September 12, 2006

Rolling with Spring and Hibernate, part 3: controller and view

So, now that we have a domain model with CRUD ability, we move forward to the controller and view parts. In the spirit of "convention over configuration", Spring 2.0 introduces a brand new URL mapper (the component responsible for determining what url (patterns) are handled by which controller): the ControllerClassNameHandlerMapping. Basically, it maps /foo/* requests too FooController, if FooController is a MultiActionController. We're going to write exactly such a RecipeController for the operations list, create, edit, store and delete.

For the views, I've chosen Freemarker, a fast and expressive template engine that has all the benefits of Velocity, the ability to use JSP taglibs and much more.

Additionally, I've chosen to add the OpenSessionInViewInterceptor, since all Rails relationships are lazy by default. It will allow Hibernate to load relations on the fly when needed, by keeping a session open during the request.

Rails controllers work exactly like the ControllerClassNameHandlerMapping, which is very handy since whenever you need an action /foo/doFunkyStuff, you just add that method to FooController and it will be exectued on those requests. Rails view are (always) .rhtml, which is Ruby scriptlets embedded in HTML, so both the handler mapping and the view configuration are things that work out of the box in Rails. Here's what our view and handler mapping configuration will look like (it goes into applicationContext-mvc.xml and cookbook-servet.xml, respectively):

<bean id="freeMarkerConfigurer" class="org.springframework...FreeMarkerConfigurer">
<property name="configLocation" value="/WEB-INF/classes/freemarker.properties" />
<property name="templateLoaderPath" value="/WEB-INF/freemarker" />
</bean>

<bean id="viewResolver" class="org.springframework...FreeMarkerViewResolver">
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="requestContextAttribute" value="rc" />
<property name="contentType" value="text/html; charset=UTF-8" />
</bean>

<bean id="openSessionInViewInterceptor" class="org.springframework...OpenSessionInViewInterceptor" />

There is a freemarker.properties which you can use to specify auto includes/imports, date formatting and some other things, but the defaults should do fine here.

Finally, the handler mapping:

<bean class="org.springframework...ControllerClassNameHandlerMapping">
<property name="interceptors" ref="openSessionInViewInterceptor">
</property>

<bean id="recipieController" class="se.petrix.cookbook.controller.RecipeController">

which is, as you can see, basically nothing. We simply rely on the class name, and add the OSIV interceptor to all handlers. The "interceptors" property is actually a list, but when you have a list with only one element, you can put it in a value or ref attribute.

We'll put our Freemarker templates in /WEB-INF/freemarker/recipe, and since we don't have any view scaffolding, we need to write them ourselves. One view for listing, and one view that we use for both edit and create. Freemarker does not handle null values, so we're going to make use of the "?if_exists" built-in, which expands to a GeneralPurposeNothing, a wrapper that usually has the meaning that you think it has, i.e. ${myList?if_exists} becomes an empty list, ${myString?if_exists} becomes an empty string and so on. Here's how we list all recipies:

<#list recipies?if_exists as r>
<tr>
<td>#{r.id}</td>
<td>${r.title?if_exists}</td>
<td>${r.description?if_exists}</td>
<td>${r.instructions?if_exists}</td>
<td>${(r.date.time)?if_exists?date}</td>
<td>${(r.category.name)?if_exists}</td>
<td><a href="${rc.contextPath}/recipie/edit.htm?id=#{r.id}">Edit</a></td>
<td><a href="${rc.contextPath}/recipie/delete.htm?id=#{r.id}">Delete</a></td>
</tr>
</#list>

Here you can see the date-formatter that works on a java.util.Date (?date). Another important thing is that Freemarker formats numbers by Locale when you use ${someNumber} ("1,000" in US locale vs. "1 000" in Swedish), but prints it as a mathematical number when you use #{someNumber} ("1000"). The "rc" varable is Spring's RequestContext, a wrapper around the HttpServletRequest that you can use for accessing the context path for example.

Note: as of Freemarker 2.3.7, the "?if_exists" builtin has been superceded by the shorthand notation "!".

Now there's only one thing left: web.xml. Again, Rails does not need one, but it's not a big deal, since you rarely make any big changes to it after the first few weeks of development, and you most likely already have a skeleton to start from (if you don't, you can use mine). It says "alright, we have a Spring servlet, which we want to hand *.htm requests to, and the Spring context is specified in the /WEB-INF/classes/applicationContext-*.xml files". As with all XML files in this example application, it has a corresponding XSD which smart editors can utilize to provide syntax checking an element and attribute completion.

Package the web app inside the source directory:

mvn war:inplace

The first time you run any Maven command it will download a large number of jars, many of which are plugins. It's all cached locally, so don't worry. On deploy, Hibernate will create your database schema for you.

AspectJ load-time weaving requires a parameter to be passed to the Java VM. This can be accomplished by setting the shell variable MAVEN_OPTS before starting Jetty:

export MAVEN_OPTS=-javaagent:src/main/webapp/WEB-INF/lib/aspectjweaver-1.5.2a.jar

Adapt to your shell of choice. Now we're ready to start the Jetty server:

mvn jetty:run

Watch the magic at http://localhost:8080/cookbook/recipe/list.htm :-)

The scan interval is set to 3 seconds, and since we're packaging the webapp inside the source directory, any changes to non-Java files are instantly available, and changes to Java files will trigger a context reload on recompilation (which your IDE should do for you). Try the old space-backspace-save trick to trigger a recompilation, and you should see a context reload.

In part 4, we'll add the Category model and relationship between Recipe and Category.

Monday, September 11, 2006

Rolling with Spring and Hibernate, part 2: the model

Most of the first part consisted of preparing the development environment, and while Rails clearly is faster here, the interesting part begins now, when we look at how much work goes into each actual modification of the application. Almost all of part 1 can be condensed into a more sophisticated Maven archetype.

The first thing we'll do now is to begin building our domain model. As I said, we'll use Hibernate with annotations as our ORM, and were going to let the model drive the database design. Hibernate is configured with url, driver class and so on, and additionally we enable the automatic schema updating from mapping metadata:

hibernate.hbm2ddl.auto=update

in hibernate.properties. Another useful setting is dumping formatted SQL to standard out, so that we can easily inspect the generated SQL:

hibernate.show_sql=true
hibernate.format_sql=true

A new feature of Spring 2.0 is the ability to wire collaborators into domain model objects on instantiation, using the @Configurable annotation and AspectJ load-time weaving. Were going to use this to inject a Hibernate SessionFactory into the domain model, allowing use to completely skip the DAO layer and use a syntax similar to ActiveRecord, with CRUD metods in the domain object. For simple application such as this one, we don't need any additional abstraction on top of Hibernate.

Populate the Recipe class with a few properties, and make it an @Entity:

@Entity
@Configurable(autowire=Autowire.BY_NAME)
public class Recipe {

String title;
...
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
...
}

Yes, we're missing an id, but we're going to build a BasicEntity base class that provides CRUD for both our Recipe and Category classes. The main point here is that you simply add a property to a Java class marked as @Entity, and Hibernate will modify the database schema on deploy. This is contrary to Rails, where you modify the schema by hand and get the model properties on the fly. For a simple model, either way is sufficient and roughly equivalent (they follow the DRY principle), but as an application grows in complexity you may want to diverge a little from the default behaviour.

The CRUD-providing base class looks like this:

@MappedSuperclass
public abstract class BasicEntity implements Serializable {

private Long id;
protected SessionFactory sessionFactory;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

@Transient
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}

@Transient
protected Session getSession() {
return sessionFactory.getCurrentSession();
}

@Transient
protected Criteria getCriteria() {
return getSession().createCriteria(this.getClass());
}

@SuppressWarnings("unchecked")
@Transactional(readOnly=true)
public T load() {
getSession().refresh(this);
return (T) this;
}
...
// + methods for store, delete and findAll

}
There are many interesting things here: the @MappedSuperclass annotation allows us to use this as a base class for mapped entities, with the Id property being mapped in subclasses. We're using generics to make return values match the inherited class, a varargs parameter for relational properties to join (relations are lazy by default in Hibernate 3), and we're using the @Transactional annotation to specify that the load() method should be run in a transaction.

In other words, theres a lot going on here. But maybe the most important thing is the SessionFactory, which of course is the Hibernate interface through which you perform the actual database operations. On to the application context: here's what we need to inject a SessionFactory into all Recipe instances, even those created outside of our control, and to make all @Transactional methods transactional:

<bean id="transactionManager" class="org.springframework...HibernateTransactionManager">

<bean class="org.springframework...AnnotationBeanConfigurerAspect" method="aspectOf">

<bean class="org.springframework...AnnotationTransactionAspect" method="aspectOf">

<bean id="sessionFactory" class="org.springframework...AnnotationSessionFactoryBean">
<property name="annotatedClasses">
<list>
<value>se.petrix.cookbook.model.Recipe</value>
</list>
</property>
</bean>


In other words, we need a transaction manager (since @Transactional isn't tied to any particular transaction implementation), a SessionFactory of course, and finally the two AspectJ aspects that are applied to classes and methods with the @Configurable and @Transactional annotations. All of this goes into applicationContext-hibernate.xml. For the AnnotationBeanConfigurerAspect bean, we could use the shorter form:

<aop:spring-configured />

but since there's no corresponding short version for the transaction aspect (<tx:annotation-driven/> is for Spring AOP, not AspectJ...hopefully that'll change in the future). In general, Hibernate configuration could benefit in a major way from an XSD schema.

Comparing to Rails, we note that none of this is needed, since you don't get to choose/have to decide what transaction strategy or ORM solution you want. One annoying thing in Spring however is that you have to manually specifiy what classes should be mapped by Hibernate, even though they are all marked with @Entity. That's one area that Spring could improve upon Hibernate, by post-processing all classes in a package hierarchy specified by a wildcard pattern, for example. More on that in the last part.

The functionality of the BasicEntity class is provided by ActiveRecord in Rails, which your mapped domain objects inherit. This is a fair amount of overhead at this stage, on the other hand we could scale this by switching to injecting a complex business layer fairly easily, creating more layers in our application.

Finally we change the Recipe class to inherit from BasicEntity:

public class Recipe extends BasicEntity<Recipe> implements Serializable {

Note the parametrization!

Rolling with Spring and Hibernate, part 1: setup

Now that the Spring 2.0 release is imminent, and we're approaching the final version of JPA with Hibernate being the most prominent implementation, I though it would be interesting to see how the Recipies application know from the excellent "Rolling with Ruby on Rails" article from ONLamp.com could be built on this upcoming stack.

Note that I'm not trying to make neither Rails nor Spring/Hibernate (intentionally) look bad, I think both stacks are very interesting. This particular domain, very-simple-web-application-with-a-quick-start really should be considered Rails' home turf, and I'm trying to determine exactly where the overhead is major, where it's minor, and maybe even where Rails is trailing. I'm also going to point out a few areas where Spring could do better.

This first part is going to describe the development setup I'm using: tools, API:s etc.

I decided to use the following API components:
And these development tools:
WST and SpringIDE are crucial for smooth XML editing, offering code-completion and validation of everything from attribute values (according to XSD) to bean-name references.

Ok, so let's get this party started! First of all, we create a database using these commands in a MySQL promt:

create database 'cookbook' character set 'utf8';
grant all on cookbook.* to 'cookbook'@'localhost' identified by 'cookbook';

Maven lets us create the basic directory structure and a basic pom.xml by running the Quickstart archetype:

mvn archetype:create -DgroupId=se.petrix -DartifactId=cookbook

Unfortunately, a few important directories are missing from this, so we create them manually:

mkdir -p src/main/resources
mkdir -p src/test/resources
mkdir -p src/main/webapp/WEB-INF

The "resource" directories hold stuff that goes into the root of the classpath in the final package. We can also get rid of the App.java and AppTest.java files.

Next, we populate the pom.xml with the API components we've chosen. Rails, of course does not really offer a choice of ORM, templating engine etc, so this is a bit more work. A slightly more advanced archetype could make this a lot easier, by generating a pom.xml with a few more dependencies and common configuration options already in place. I won't go through the entire pom.xml here, but there are a few caveats:
  • We need to set the source and target levels of the Java compiler to 1.5 explicitly, Maven assumes java 1.4.
  • The Hibernate Annotations dependency information is a bit out of date, so we need to exclude the improper dependency and include the correct one.
  • The output directory, which is propagated to Eclipse, is set to inside the web app source folder, to make the feedback cycle as short as possible.

The short feedback cycle is one of Rails' strongest points, and due to the fact that we can't reload classes on the fly in Java - a very popular RFE - we need to reload the entire web app when making a change in a Java file. It's not as bad as it may sound though, reloading is fast and automatic. Changes to Freemarker templates require no reloading, and Spring 2.0 allows you to write beans in script languages such as Groovy, BeanShell or even Ruby, which don't need reloading either.

After this is done, we generate the Eclipse project metadata using Maven:

mvn eclipse:eclipse

One of the benefits of Maven is that you can generate project metadata for many of the biggest IDE's, such as Eclipse, Netbeans and IDEA. In general I think it would be safe to say that IDE support is a lot better for the Spring/Hibernate stack than for Rails, and Maven makes it easy to move between them.

In the next part, we'll start writing our domain model.