At line 1 added 150 lines |
The initial [rather lengthy|ScriptedVSCodedWebapps] introduction to this article examined the battle between the two major camps of webapp developers (which could be described as "Scripted" vs. "Coded") in more depth than anybody who came here for a quick tutorial would be interested in. However, they would have gained from it the following points: |
|
* RSF aims to bridge the gap between the scripted and coded camps, allowing styles similar to scripted webapps to be used for simpler or early-stage projects, with a clear transition path to mature coded solutions. |
* In this tutorial we will demonstrate the full power of the Java open source community in the form of Spring, Hibernate and Maven brought to bear on the simple Ruby on Rails "cookbook" application, while seeing these sometimes uneasy partners cooperate seamlessly in an environment based on pure HTML templates and XML configuration files. In fact, this application will be implemented without %%(color:red) one single line of Java code %%. Yes, you heard that right! |
|
This sample will follow the ["Rolling with Ruby on Rails"|http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html?page=1] sample app, implementing the same functionality in a Java environment. Skip straight to [page 4|HibernateCookBook_4] to see the pure Java version of the app - the initial development uses the XML strategy for component definition. |
|
The RSF SVN repository includes several versions of this project, which show off different styles of working with RSF. |
|Project name|Description |
|[HibernateCookbook-basic|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/basic/]|Most basic project defining one entity (Recipe) together with static XML and HTML views. Suitable for copying to use as a "base project" for new RSF-Hibernate projects |
|[HibernateCookbook-xmlcategory|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/xmlcategory/]|"Completed" project with the final functionality of the ROR project, using XML producers. Two entities, "Recipe" and "Category", shows how to use RSF (HTML) selection controls to operate directly on a Hibernate bean model. |
|[HibernateCookbook-javacategory|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/javacategory-code/]|A reworking of the finished "category" project, using pure Java ViewProducers rather than XML files, the path that might be taken for a more complex project. This project is actually factored into three Maven artifacts, category-datamodel, javacategory-code and javacategory-webapp - consult [page 5|HibernateCookBook_4] for details. |
|[HibernateCookbook-xsl|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/javacategory/]|Shows the "basic" project, autogenerated from nothing but the Hibernate .hbm mapping files using XSL transforms. A suitable strategy to "kick-start" a project with a complex entity model, or where it is to be prototyped or maintained by non-developers. (TBA) |
|
In fact, while the XML strategy for defining [view components|ComponentProducers] can offer crucial flexibility in some situations, it's imagined that the majority of RSF users will want to go with pure Java producers. XML producers and Java producers are generally in one-for-one correspondance - we will first generate the complete app using the XML style, and then on the final page rework it in a Java form that is more compact and efficient. Note that view producers will in general be the only dependence in application code on the RSF framework, and the fact that these generally define "pure data" that could equally well be represented in XML indicates that this is a very light kind of dependence. |
|
!!! First, choose a database |
|
Like the ROR example, we will begin by installing a database. We choose [MySQL|http://www.mysql.com/] since it is common in production environments, and is now quite straightforward to install. Other possibilities are Postgres, or for low-profile deployments there is Apache's excellent [Derby|http://db.apache.org/derby/] (formerly IBM Cloudscape). Hibernate has drivers for [all popular databases|http://www.hibernate.org/80.html], so take your pick. |
|
The most recent version of MySQL at the time of writing was [5.0.18|http://dev.mysql.com/downloads/mysql/5.0.html] - we used the [Windows Essentials|http://dev.mysql.com/doc/refman/5.0/en/windows-installation.html] distribution. Using a different database is just a matter of changing Hibernate configuration files. |
|
[{Image src='HibernateCookBook/MySQL-install.png' align='center'}] |
|
The sample code for this project in [SVN|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/basic/] assumes that the database was set up with an "anonymous account" option. |
|
We use Hibernate together with Spring (naturally, for a Spring-based framework such as RSF), so instead of using Hibernate's property file syntax, we configure Hibernate using two beans in the application's ''applicationContext.xml'' file, being a ''dataSource'' specifying details of the database connection at the JDBC level, and the Hibernate ''sessionFactory''. |
|
The two beans required to connect to our default anonymous MySQL instance are reproduced below, taken from the full [applicationContext.xml|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/basic/src/webapp/WEB-INF/applicationContext.xml] file: |
|
{{{ |
<!-- Configure the Hibernate SessionFactory bean with a basic data source |
pointing at our database --> |
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" |
destroy-method="close"> |
<property name="driverClassName"> |
<value>com.mysql.jdbc.Driver</value> |
</property> |
<property name="url"> |
<value>jdbc:mysql://localhost:3306/hibernatecookbook</value> |
</property> |
<property name="username"> |
<value></value> |
</property> |
<property name="password"> |
<value></value> |
</property> |
</bean> |
|
<bean id="sessionFactory" |
class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> |
<property name="dataSource"> |
<ref bean="dataSource" /> |
</property> |
<property name="mappingLocations"> |
<value>classpath:conf/**.hbm.xml</value> |
</property> |
<property name="hibernateProperties"> |
<props> |
<prop key="hibernate.dialect"> |
net.sf.hibernate.dialect.MySQLDialect |
</prop> |
<prop key="hibernate.show_sql">true</prop> |
<!-- Always drop and recreate the database schema on startup --> |
<prop key="hbm2ddl.auto">update</prop> |
</props> |
</property> |
<!-- Always drop and recreate the database schema on startup --> |
<property name="schemaUpdate"><value>true</value></property> |
</bean> |
}}} |
|
Deploying a Hibernate app requires a great deal more in the way of supporting libraries than a simple RSF app. Luckily, maven makes this easy - rather than figure this out from scratch, simply use the standard [project.xml|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/basic/project.xml] file for our basic project which will automatically populate your Maven repository and deployed project with the correct jars. I have also usefully commented the purposes and requirers of each of the dependencies. |
|
We use Hibernate 2.x since the Hibernate 3.x toolset is still unstable (3.1 beta 4 at time of writing), and crucially is lacking support for Maven 1.x in the hbm2java task we will use in the next section. |
|
!!! In the beginning, there was the .hbm file |
|
While the ROR version of this app is built directly on the database schema, we will take a step backwards in abstraction and take advantage of the considerable insulation and portability that Hibernate offers us. Moving your app to another database and environment is as easy as changing your Hibernate configuration file - putting first-class support for a vast plethora of second-level cache and Enterprise architectures and virtually every popular database dialect within easy reach. By comparison, ROR's "ActiveRecord" ORM solution while initially slick, is ultimately much more limited. While being "configuration-free" as ROR advertises brings startup time down to zero, later on in a project the lack of explicitness will bite with a vengeance - browse over to [The Persistence Showdown|http://www.theserverside.com/articles/article.tss?l=RailsHibernate] for a lowdown on the pros and cons. RSF is "as configuration free" as possible, but no more free than that. |
|
Anyone needing a quick start with Hibernate could consult this tutorial by [Michael Gloegl|http://www.gloegl.de/5.html], the ultimate reference of course being the [Hibernate documentation|http://www.hibernate.org/hib_docs/v3/reference/en/html/index.html]. |
|
Hibernate is designed with a very broad range of workflows in mind, as regards specification and construction of the schema. The following [ONJava article|http://www.onjava.com/pub/a/onjava/2005/12/14/hibernate-class-generation-with-hbm2java.html] by John Ferguson Smart outlines a few of them - we will choose the last of those he mentions, based on using Hibernate's ".hbm" mapping file as the starting point and authoritative specification of our data model. This approach has many advantages, which Johnathan outlines in some detail. |
|
The .hbm file will tie down once and for all the relationship between Java types and SQL, so let us write it with no further ado. If you are building this app from scratch, download, unpack and rename the "HelloWorld" app and type the following into {{/WEB-INF/cookbook.hbm.xml}} - |
|
{{{ |
<?xml version="1.0"?> |
<!DOCTYPE hibernate-mapping PUBLIC |
"-//Hibernate/Hibernate Mapping DTD 2.0//EN" |
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"> |
|
<hibernate-mapping package="uk.org.ponder.rsf.cookbook"> |
|
<class name="Recipe" table="recipe"> |
<id name="id" type="int"> |
<generator class="native" /> |
</id> |
<property name="title" type="string" not-null="true" length="255" /> |
<property name="description" type="string" length="255" /> |
<property name="date" type="date" /> |
<property name="instructions" type="string" /> |
</class> |
|
</hibernate-mapping> |
}}} |
|
This specifies the database schema that will be in effect by the time you reach figure 34 of [page 3|http://www.onlamp.com/pub/a/onlamp/2005/01/20/rails.html?page=3] of the ROR example, although in a somewhat more portable way. |
|
The next step is to embody this schema at both ends - both as Java code and as a database table. Luckily, Hibernate comes supplied with tools to perform this automatically, making good on our promise of IDE-free development. The tool to automatically generate Java classes from {{.hbm}} is part of the standard Hibernate distribution called hbm2java, for which there is useful tutorial at [OnJava|http://www.onjava.com/pub/a/onjava/2005/12/14/hibernate-class-generation-with-hbm2java.html], which explains the basic idea. Ant is fully integrated with Maven 1.x, so we can invoke the hbm2java task with the following [maven.xml|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/basic/maven.xml] definition: |
|
{{{ |
<goal name="generate-hibernate-javamodel"> |
<j:set var="hibernate.cfg.xml" |
value="${basedir}/src/conf/hibernate.cfg.xml" /> |
<j:set var="generated.hbm" value="${basedir}/src/conf/" /> |
<j:set var="generated.sources" |
value="${basedir}/src/generated-java/" /> |
<echo>${generated.sources}</echo> |
|
<ant:taskdef name="hbm2java" |
classname="net.sf.hibernate.tool.hbm2java.Hbm2JavaTask" |
classpathref="maven.dependency.classpath" /> |
|
<ant:hbm2java output="${generated.sources}"> |
<ant:fileset dir="${generated.hbm}"> |
<ant:include name="**/*.hbm.xml" /> |
</ant:fileset> |
</ant:hbm2java> |
</goal> |
}}} |
|
This task is set up to read all hibernate mapping files from our {{conf}} directory and output corresponding .java files for each entity into the {{generated-java}} directory. |
|
Invoke it using the command line |
%%( background-color: #ffc0c0; border: 1px dotted black; padding: 0.2em; ) |
{{maven generate-hibernate-javamodel}} |
%% |
issued from the project root. |
|
Voila!! The [only java code|https://saffron.caret.cam.ac.uk/svn/projects/RSFHibernateCookbook/trunk/basic/src/generated-java/uk/org/ponder/rsf/cookbook/Recipe.java] we will use in our project has just been automatically written for us. |
|
Read on to see how we will generate our first views and templates. |
---- |
Head - Hibernate Cookbook\\ |
Page 1 - [Query beans, templates and views|HibernateCookBook_1]\\ |
Page 2 - [Switches, Replicators and transit beans|HibernateCookBook_2]\\ |
Page 3 - [The main app - entity selectors and Javascript|HibernateCookBook_3]\\ |
Page 4 - [Let's hear that one more time - this time in Java|HibernateCookBook_4]\\ |
---- |