implements Elegance {

// Elwyn Malethan's musings on software development, mountain biking and general navel–gazing...

Articles tagged with 'rails'

Introducing SeemoreJ: Part #2 CRUD

In my last post about SeemoreJ I gave an account of how one might bootstrap a new web project using the seemorej–example–archetype. In this post I‘ll introduce how quick and easy it is to get a scaffolded CRUD application up and running.

I‘ll do this by stepping through the creation of the Person POJO (so pervasive in tutorials and demos), with Hibernate/JPA annotations as an example of a persistent model and – in the process we‘ll cover the default configuration of database interaction.

The model

Create a package called com.mycompany.model and add this class there.

package com.mycompany.model;

import org.hibernate.validator.NotEmpty;
import org.hibernate.validator.Email;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Entity
public class Person implements Serializable {

    private Long id;
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    private String emailAddress;

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

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

    @NotEmpty
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @NotEmpty
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Temporal(TemporalType.DATE)
    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    @NotEmpty
    @Email
    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
}

There‘s a lot of annotation in this POJO. Some for JPA persistence and some for Hibernate Validator, which the Hibernate specific SeemoreJ package uses as the validation framework. We‘ll see shortly what these validation annotations do for us.

Persistence

Mapping

Add this line to hibernate.cfg.xml within the session-factory element.

<mapping class="com.mycompany.model.Person" />

Database connection

Now to configure the database connection. I‘m assuming you have MySQL installed and that this is what you‘re going to use. Find the following section in the POM (pom.xml) and change the values to something appropriate.

<!-- ... -->
    <properties>
        <!-- ... -->
        <hibernate.dialect>org.hibernate.dialect.MySQL5InnoDBDialect</hibernate.dialect>
        <jdbc.groupId>mysql</jdbc.groupId>
        <jdbc.artifactId>mysql-connector-java</jdbc.artifactId>
        <jdbc.version>5.0.5</jdbc.version>
        <jdbc.driverClassName>com.mysql.jdbc.Driver</jdbc.driverClassName>
        <jdbc.url>jdbc:mysql://localhost/myapp?createDatabaseIfNotExist=true&amp;amp;useUnicode=true&amp;amp;characterEncoding=utf-8</jdbc.url>
        <jdbc.username></jdbc.username>
        <jdbc.password></jdbc.password>
    </properties>
<!-- ... -->

CRUD Scaffold

Add the following class to the com.mycompany.app. Whilst this is a great deal more verbose than it would be in Rails, this is all you need to have CRUD functionality for a Hibernate mapped class. It certainly beats implementing each action required. My intention is replace this implementation inheritance with some sort of configuration, probably annotation. This would make the resulting controller classes more flexible and nicer to use.

package com.mycompany.app;

import com.malethan.seemorej.hibernate.crud.CrudControllerHibernate;
import com.mycompany.model.Person;

public class PeopleController extends CrudControllerHibernate<Person, Long> {
    public PeopleController() {
        super(Person.class, Long.class);
    }
}

Now start Jetty up using mvn jetty:run-exploded. If you‘re running it for the first time you‘ll have to wait for all the dependencies to download. Once it‘s up and running got to http://localhost:8080/people/list.html

What you‘ll see is a rudimentary, fairly ugly – if I‘m honest – CRUD application. On my To–do list is to make it more attractive and – through configuration – provide some customisation. Also, I‘d like to support one–to–many, many–to–many and many–to–one relationships.

In my next post about SeemoreJ I‘ll be talking about action filters and how they‘re implemented in SeemoreJ.

First published on Apr 30, 2009. Last updated on: Dec 30, 2009.

Production configuration using Maven Profiles

Recently I had the not altogether unfamiliar requirement to have two completely different database configurations for my development environment and the live/production environment. It wasn‘t just JDBC connection details either. Locally, my Hibernate configuration was using a direct JDBC connection. The production environment requires the use of a container-managed data source.

What I could do, of course, is manually change the Hibernate configuration each time I deploy. However, I‘m lazy (a quality, which if applied correctly is good in a software developer) and I hate performing tasks that expensive electronics I‘ve bought should be able to do quicker and better than me. So I drew on my experience with Rails and had a look what was available.

The Rails way

One thing Rails does very well is the separation of production and development environments (as well as test). It allows you to configure different database connections and parameters, configure different behavior for your software (e.g. put ActiveMerchant into :production mode) and also has sensible defaults for logging levels etc. It does this by having different bootstrapping files each intuitively called development.rb, production.rb and test.rb. All in a folder conveniently called environments.

Though I‘ve never had cause to myself, you can add new environments of your own and reconfigure Rails to use it. One useful additional environment would be staging perhaps for running on the test server, we certainly wouldn‘t want ActiveMerchant creating real transactions for our end-user testing.

Maven

Maven 2 is fairly ubiquitous in Javaland now. With significant improvements over Ant, it's quickly becoming the Maker of choice. Since the project in question employs Maven for building anyway it is the obvious place to start looking for a solution.

Profiles

Maven profiles allow a wide variety of the project configuration to be overridden given the activation of a profile. This allows almost wholesale changes to the way a project is built, it‘s dependencies and even where it references it‘s sources.

My Solution

To recap, in my development environment, Hibernate uses a direct JDBC connection, configured like this:

<hibernate-configuration>
    <session-factory>
        <property name="connection.url">jdbc:mysql://localhost/my_db</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.username">dev</property>
        <property name="connection.password">dev</property>
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>

        <!-- all the mappings  -->

    </session-factory>
</hibernate-configuration>

But my live environment uses a container-managed data source, configured like this:

<hibernate-configuration>
    <session-factory>
        <property name="connection.datasource">java:comp/env/jdbc/live_datasource</property>
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>

        <!-- all the mappings  -->

    </session-factory>
</hibernate-configuration>

Filtering resources

If it was a simple matter of changing connection details I could filter the resources by using Maven filtering replacing connection details with defined properties from pom.xml. Using something like the following in pom.xml ...

<project  ... >

    <!-- ... -->

    <build>
      <!-- ... -->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>hibernate.cfg.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>hibernate.cfg.xml</exclude>
                </excludes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <!-- ... -->
    </build>

    <!-- ... -->

    <profiles>
        <profile>
            <id>env-production</id>
            <activation>
                <property>
                    <name>env</name>
                    <value>production</value>
                </property>
            </activation>
            <properties>
              <jdbc.url><![CDATA[jdbc:mysql://localhost/live_db]]></jdbc.url>
              <jdbc.username>live_user</jdbc.username>
              <jdbc.password>live_pass</jdbc.password>
            </properties>
        </profile>
    </profiles>

    <properties>
        <!-- ... -->
        <hibernate.dialect>org.hibernate.dialect.MySQL5InnoDBDialect</hibernate.dialect>
        <jdbc.driverClassName>com.mysql.jdbc.Driver</jdbc.driverClassName>
        <jdbc.url><![CDATA[jdbc:mysql://localhost/my_db]]></jdbc.url>
        <jdbc.username>dev</jdbc.username>
        <jdbc.password>dev</jdbc.password>
    </properties>
</project>

... and the following in hibernate.cfg.xml ...

<hibernate-configuration>
    <session-factory>
        <property name="connection.url">${jdbc.url}</property>
        <property name="connection.driver_class">${jdbc.driverClassName}</property>
        <property name="connection.username">${jdbc.username}</property>
        <property name="connection.password">${jdbc.password}</property>
        <property name="dialect">${hibernate.dialect}</property>
        <!-- DB schema will be updated if needed -->
        <property name="hbm2ddl.auto">update</property>

        <!-- all the mappings  -->

    </session-factory>
</hibernate-configuration>

Using this method I could run mvn package -Denv=production and a WAR would be generated with the live database configuration populated into my hibernate.cfg.xml. However, in my case the Hibernate configuration doesn‘t have the same properties.

Defining different production resources

The answer I came up with is to define some completely separate resources for live packaging. Because I didn‘t want all the common configuration – like the mappings – duplicated I‘ve moved some of the database configuration into hibernate.properties. So now I have the following in my pom.xml ...

<project  ... >

    <!-- ... -->

    <build>
        <!-- ... -->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>hibernate.cfg.xml</include>
                    <include>hibernate.properties</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>hibernate.cfg.xml</exclude>
                    <exclude>hibernate.properties</exclude>
                </excludes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <!-- ... -->
    </build>

    <!-- ... -->

    <profiles>
        <profile>
            <id>env-production</id>
            <activation>
                <property>
                    <name>env</name>
                    <value>production</value>
                </property>
            </activation>
            <build>
                <resources>
                    <resource>
                        <directory>src/production/resources</directory>
                        <includes>
                            <include>hibernate.properties</include>
                        </includes>
                        <filtering>true</filtering>
                    </resource>
                </resources>
            </build>
            <dependencies>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.0.5</version>
                    <scope>provided</scope>
                </dependency>
            </dependencies>
            <properties>
                <hibernate.datasource>java:comp/env/jdbc/live_datasource</hibernate.datasource>
            </properties>
        </profile>
    </profiles>

    <properties>
        <!-- ... -->
        <hibernate.dialect>org.hibernate.dialect.MySQL5InnoDBDialect</hibernate.dialect>
        <jdbc.driverClassName>com.mysql.jdbc.Driver</jdbc.driverClassName>
        <jdbc.url><![CDATA[jdbc:mysql://localhost/my_db]]></jdbc.url>
        <jdbc.username>dev</jdbc.username>
        <jdbc.password>dev</jdbc.password>
    </properties>
</project>

... and the following files in src/main/resources and src/production/resources respectively:

Development Hibernate Configuration
hibernate.connection.url=${jdbc.url}
hibernate.connection.driver_class=${jdbc.driverClassName}
hibernate.connection.username=${jdbc.username}
hibernate.connection.password=${jdbc.password}
Production Hibernate Configuration
hibernate.connection.datasource=${hibernate.datasource}

You may notice an additional/replacement dependency in the production profile. This is because for container managed data sources the JDBC driver needs to be known to the container before it loads any web applications. Therefore, in Tomcat‘s case it should already be in the common/lib folder.

Using Maven profiles may not be the best solution for achieving different database configuration. However I think it definitely has promise in terms of a more general environment-specific packaging/deployment.

The Maven website has a good article that adopts a different approach to building for different environments here.

First published on Nov 26, 2008. Last updated on: Dec 30, 2009.

ActiveRecord timestamps using a Hibernate Interceptor

Having developed a lot of web applications of various sizes using both Ruby on Rails and Java-based web frameworks, I‘ve noticed a few differences. One is – for better or worse – that the frameworks in Rails tend to do a lot more for you out of the box.

One quite cool feature of ActiveRecord is it's implicit population of created_on and updated_on columns if they are present.

Well I decided that I‘d quite like to have equivalent columns for my articles on this website. Obviously since I‘m developing in Java I‘ll adopt the appropriate variable-name convention of updatedOn and createdOn, like so.

/* ... */
@Entity
public class BlogPost {

    /* ... */

    Date createdOn;
    Date updatedOn;

    /* ... */

    @Temporal(value = TemporalType.TIMESTAMP)
    public Date getCreatedOn() {
        return createdOn;
    }

    public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }

    @Temporal(value = TemporalType.TIMESTAMP)
    public Date getUpdatedOn() {
        return updatedOn;
    }

    public void setUpdatedOn(Date updatedOn) {
        this.updatedOn = updatedOn;
    }

    /* ... */
}

My main motivation for adding these fields is so I can show if an article has been modified since it was released or simply send the HTTP headers indicating the last time an article was edited, like so:

// e.g. Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
getResponse().setHeader("Last-Modified",
                        new SimpleDateFormat("EEE, MMM yyyy HH:mm:ss z").format(blogPost.getUpdatedOn()));

Given the limited scope of a meager website such as mine, I could have easily just added these fields to my persistent class and added a filter to the update and create actions of my web application. Even simpler I could have added the code directly to the actions, though this would be less DRY. In either approach the changes would only apply to the the persistent entity representing the main articles of the site and I would have to repeat it when I add things such as comments. So I went for something more scalable.

Hibernate Interceptors

A couple of years ago, when I was with Beanlogic, I had an activity log requirement for customer. We had implemented a multi-user system and the administrators wanted to know about everything their users were up to. We were using Hibernate then and I'm using it for my site as well. What we used it to record an activity log then can also be used for my requirements here. That is an Interceptor.

Here‘s what my interceptor looks like:

package /* ...*/

public class TimestampInterceptor extends EmptyInterceptor {
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState,
                                Object[] previousState, String[] propertyNames,
                                Type[] types) {
        setValue(currentState, propertyNames, "updatedOn", new Date());
        return true;
    }

   public boolean onSave(Object entity, Serializable id, Object[] state,
                          String[] propertyNames, Type[] types) {
        setValue(state, propertyNames, "createdOn", new Date());
        setValue(state, propertyNames, "updatedOn", new Date());
        return true;
    }

    private void setValue(Object[] state, String[] propertyNames, String propertyToSet, Object value) {
        int index = Arrays.asList(propertyNames).indexOf(propertyToSet);
        if (index >= 0) {
            state[index] = value;
        }
    }
}

onFlushDirty deals with any updates to existing records and onSave deals with any new INSERTs of records. This means that if the fields are present in any of our persistent objects then they'll be populated. Even better, it will silently fail for any entities that don't have the timestamp columns. Note: onFlushDirty won't trigger if an update is performed where no properties have changed. So bear this in mind when testing.

When I first used this method for the activity log a drew a lot of inspiration from Using a Hibernate Interceptor To Set Audit Trail Properties, it was pretty useful and it goes into a little more detail. You may (will) notice significant similarities in implementation, particularly the setValue method.

First published on Nov 21, 2008. Last updated on: Dec 30, 2009.

 
People I like
Other sites