Getting Started

Gareth Reeves


Why get started?

So you have a object that you want store in a relational database or you have some data in a relational database that you would like to read into a object. It is quite easy to write some SQL statements and send them to a database connection to have them executed. You could put this code in a method on the object called load() or save(). This is a common approach and often is quite adequate. It is convenient for the clients of the object to simple call the save() method when they are done making changes. It manages dependencies nicely, the clients of don't need to know about SQL or the DriveManager or the Connection.
public class Foo {
    String bar;
    public void load() {
    	// code to load attributes from the database
    }
    public void save() {
    	// code to save attributes into database
    }
    public void setBar(String aBar) {
    	bar = aBar;
    }
    public String getBar() {
    	return bar;
    }
}
However this breaks down when you start querying based on different attributes or when you have relationships between the objects. When you have more than a couple of objects that manage their own persistence you start to loose cohesion of the persistence code and it can become hard to change. If you have a number of these objects and are suffering when you try to change stuff, jStorm is for you. It is time to get started.

Mapping

Define a mapping object for the object that you want to persist.
public class Foo {
    String bar;

    public void setBar(String aBar) {
    	bar = aBar;
    }
    public String getBar() {
    	return bar;
    }
}
jstorm.mapping.Mapping mapping = new jstorm.mapping.Mapping();
mapping.setTableName( "FooTable" );
mapping.setObjectClass( Foo.class );
mapping.addAttribute( "bar", "BarColumn", String.class );
Now you can use jStorm to load from and insert Foo into the database.
jstorm.mapping.SqlRunner runner = new jstorm.mapping.SqlRunner();
runner.doSelect( mapping );
The above code will return a new Foo object for each row in the FooTable table with the value from BarColumn set as the bar attribute on the object returned.
Foo foo = new Foo();
foo.setBar( "Bar" );

jstorm.mapping.SqlRunner runner = new jstorm.mapping.SqlRunner();
runner.doInsert( foo, mapping );
The above code will insert a new row into the FooTable table and set "Bar" as the value of the BarColumn.

Updates and Deletes

To update or delete an object, the object must have a key.
public class Foo {
    String bar;
    long primaryKey;

    public void setBar(String aBar) {
    	bar = aBar;
    }
    public String getBar() {
    	return bar;
    }
    public void setPrimaryKey(long aKey) {
	  	primaryKey = aKey;
	}
	public long getPrimaryKey() {
	  	return primaryKey;
    }
}
To identify an attribute as a key you need to create the mapping as follows.
jstorm.mapping.Mapping mapping = new jstorm.mapping.Mapping();
mapping.setTableName( "FooTable" );
mapping.setObjectClass( Foo.class );
mapping.addAttribute( "bar", "BarColumn", String.class );
mapping.addAttribute( "primaryKey", "Key", Long.TYPE, true );

The 4th boolean parameter tells jStorm that the attribute will be a key. If only a single key is set on a mapping then it will be treated as a primary key and will automatically generate a unique value. If there are more than one keys set on the mapping they will be treated as composite keys and the initial values need to be set explicitly.

Now we can update and delete objects

// Assume foo read from database
foo.setBar( "ChangedBar" );

jstorm.mapping.SqlRunner runner = new jstorm.mapping.SqlRunner();
runner.doUpdate( foo, mapping );
// Assume foo read from database
jstorm.mapping.SqlRunner runner = new jstorm.mapping.SqlRunner();
runner.doDelete( foo, mapping );

The Database Factory

jStorm has a factory that can be used to get instances of jStorm or of JDBC Connections.

jstorm.factory.IDatabase database = jstorm.factory.DatabaseFactory.getDatabase();
database.doSelect( mapping );

Instead of creating a new instance of the SqlRunner, the prefered method is to get a IDatabase instance from the factory. IDatabase is an interface that SqlRunner implements. The major advantage to using the factory is that we can substitute SqlRunner for other things that implement the IDatabase interface. For example, we may decide that instead of persisting to a relational database we may want to persist to an object database or even to XML.

Unit Testing

Another example of when we would want to subsitute the jStorm SqlRunner for something else is when we are unit testing. When writing unit tests it is often important to sepearate the code into isolated units. Isolating a unit means that when we execute a section of code for test purposes it shouldnt leak out into other parts of the code. For example, if I have class A that collaborates with class B but I want to test class A in isolation then I need to some how stop A from calling B directly during that testing. When the code is running in a production mode it needs to switch back to calling B directly. To achieve the desired behaivor we need to be able to easily switch back and forth between unit testing mode and production mode.

It is often desirable to run unit tests without connecting to directly to the database. There is a strong dependency between the tests and the data that they operate with. When a team of developers is sharing a database that contains test data it is common for that data to get out of sync and for tests to fail when the code is actually executing as expected. Instead of sharing data we prefer to each have our own copy. One way to do this is to have a database instance to each developer and a build script that inserts the correct test data into the database before running the tests. Unfortunately it is not usually practical to have a database instance to each developer. In addition we want our tests to run really quickly so that we can run them a lot and get really good feedback about the state of the system. Working with the database can significantly slow the tests down.

The prefered solution to supplying data for the unit tests is within each of the unit tests themselves. For example, I want to test a class that updates customer information. I would supply the unit to be tested with a customer object, run the code, then check that the customer had been changed appropriately.

Mock Database

The MockDatabase is an object in jStorm that pretends to be a database. You give it data to be returned when other objects request data from the database (doSelect) and you can check data that gets passed to the database for persistence (doInsert).

You can also tell the database factory to give out instances of a MockDatabase instead of the regular jStorm SqlRunner.

jstorm.factory.MockDatabase mockDatabase = new jstorm.factory.MockDatabase();
mockDatabase.addReturnObject( new Customer() );

jstorm.factory.DatabaseFactory.setDatabase( mockDatabase );

When a object that needs to persist something calls the database factory to get a instance of a IDatabase, the mockDatabase object will be returned. But the calling object wont know the difference! When the calling object calls doSelect on the database the new Customer() object will be returned. The MockDatabase contains a list of objects that will be returned so that you can setup a MockDatabase that recieve many requests from calling code.