EQUIP2 and Hibernate

Chris Greenhalgh 2006-01-25; last updated 2007-10-24

Introduction

EQUIP2 provides a combined state/event management facility for building reactive and loosely coupled (typically distributed) appplications. Elements of EQUIP2 are inspired by and to some extend modelled on the Hibernate Object/relational mapping system, for example the use of POJOs/JavaBeans rather than framework specific object classes, the session/transaction-oriented API and the managed/unmanaged object distinction. However, there are also differences, which currently include:
There is a first version of an EQUIP2 dataspace implementation over a Hibernate Object/relational store. The goals of this are:
This document describes design notes, plans, and progress to date.

Current status and usage

There is an initial implementation: equip2.persist.hibernate.j2se.PersistentDataspace. It is currently quite limited:

The sample server is equip2.persist.hibernate.test.j2se.PersistentDataspace. This will read hibernate configuration from the file hibernate.cfg.xml in the root of the classpath. A standard sample is hibernate.cfg.xml. This file includes details of the underlying Relational database to use (via JDBC) and references to all of the individual object class mapping files to be used: these must be provided for each class to be stored in Hibernate (see for example MyDataType.hbm.xml for equip2.net.test.MyDataType.

Using HSQLDB

HSQLDB is a pure java database which is very convenient to use as an in-process file-backed database. This is the default (now) for EQUIP2. The following extract from the configuration in hibernate.cfg.xml configures the persistent dataspace to use HSQLDB and in particular the file(s) hsql_test_db.*:
       <!-- HSQLDB in-process database - simple/testing/development -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:file:hsql_test_db</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>

Note that the username & password are standard HSQLDB defaults for an in-process database.

The nice thing about this is that no separate install is required. However, HSQLDB may be less durable than mySQL in the presence of some failures. It is also unclear how well its performance scales to larger dataspaces.

The ant target hibernate_run will run a test application against the default persistent dataspace, initially HSQLDB as configured above.

Using mySQL

mySQL is a popular freely-available database. It requires a separate download and install. It also requires that the database to be used be explicitly created within the (logically shared) mySQL database server.

To get unicode support with Hibernate and mySQL one known working method is to set the database's default character set to utf8 before creating the database and running Hibernate. For example, this may be specified by the line:

[mysqld]
...
default-character-set=utf8
default-collation=utf8_bin

in the configuration file my.ini, which may (depending on installation/version) be found in C:\Program Files\MySQL\MySQL Server 5.0.

Before using a mySQL-backed persistent dataspace the database must have been set up as per the configuration in hibernate.cfg.xml, for example on mySQL you might do:

create database equip2 character set utf8  collate utf8_bin;
grant all on equip2.* to equip2@localhost identified by 'equip2';
flush privileges;

which would correspond to the (commented out) configuration:

	<!-- mysql - production -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql:///equip2</property>
<property name="connection.username">equip2</property>
<property name="connection.password">equip2</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>

Once the configuration has been done and the database created the same ant test target can be used (hibernate_run).

Creating Hibernate mappings

The simple bean generator now has support for generating Hibernate mapping files as well as java beans and EQUIP2 helper classes; see Simple_Bean_Generator.html.

All classes to be used in the persistent dataspace must have their own hibernate mapping files, which must be listed in the hibernate.cfg.xml configuration file, e.g.:

        <mapping resource="equip2/net/test/MyDataType.hbm.xml"/>
<mapping resource="equip2/persist/hibernate/test/j2se/HibernateTestObject.hbm.xml"/>
<mapping resource="equip2/persist/hibernate/test/j2se/HibernatePerfTestObject.hbm.xml"/>

Changing Mapped classes

If mapped classes are changed (e.g. properties added, removed or changed) then Hibernate may be able to continue using and update an existing database. However it does not handle some changes correctly; the safest option is to delete the old database (or all of its tables) and allow it to create new tables for the latest types and mappings.

Mapping EQUIP2 and Hibernate

These are mainly my working notes.

Common object support

EQUIP2 requires (currently on J2SE, and always on J2ME) a simple helper class for each POJO/JavaBean class that will be used in a dataspace. This helper class describes the structure of the object (its fields/properties), provides reflection-like set/get access to those fields, performs matching between values of that type and templates values, and extracts the 'identity' value (if any) from an object of that type.

Hibernate requires a description of the O/R mapping for each POJO/JavaBean class, for example a corresponding .hbm.xml mapping file. This identifies the class's properties to be managed by hibernate, any non-default SQL types or properties (e.g. not null) and any database ID (primary key).

There are some issues with the definition and use of equals() and hashCode(). In particular, Hibernate requires them to be defined for objects that are to be used in Sets, but also recommends that they consider "only the properties that form the business key, a key that would identify our instance in the real world (a natural candidate key)". However, EQUIP2 current relies on object equals to check whether managed objects have been changed - which should include all properties of the object (other than any Hibernate dataspace-allocated ID property).

There is an initial implementation of Sets, but there are some performance issues with mapping queries on this to Hibernate.

Operation correspondence

Add

EQUIP2 ISession.add is directly equivalent to Hibernate save(Object). In the first case the added object becomes managed by the EQUIP2 session; in the latter case the saved object becomes managed by the Hibernate session.

Note however that the EQUIP session add operation is performed locally in the session, and only executed as a change in the session end operation.

Get

EQUIP2 ISession.get is similar to Hibernate load(Class clazz, Object key) but with some differences:

In order to map EQUIP2 get to Hibernate load we would need to have configured the EQUIP2 identity as the Hibernate (application-defined) key, and know this.


Alternatively and more generally, a query will be required to give the effects of get? The two cases are:

In the first case (no identity) it MIGHT be possible to do an update() for a new object (or merge(), or retrieval of previously retrieved managed object for a object already in the session). Alternatively an exact query would be required. This would be more restrictive than a normal match because NULL must also match exactly.

In the second case (identity defined) some kind of query will be required, in the current case on the key element field's value.

Remove

EQUIP2 ISession.remove corresponds directly to Hibernate delete. Note however that the argument to delete must be a hibernate-managed object. EQUIP2 remote operations are queued for enactment in sessionEnd; they must have been managed within that session. Consequently the EQUIP2 session will have included a prior add, which is then discarded internally, or a prior get or match which returned the object. Note that with a remote session the object being offered for deletion will be a (twice-removed) copy of the Hibernate-managed object which presumably corresponded to the result of the get/match.

Will delete work anyway? Or can the copy become managed using merge()? (update() should fail because the previous managed value should be there) Or we could keep a cache of the returned objects and map back to the local managed object by hashCode/equals.

It appears that Hibernate update() (or lock()) will not work at all if the java object does not hold the DB identity. So the old local managed object will need to be retrieved again (currently by an exact query) and then removed.

Match

EQUIP2 ISession.match has two main issues to deal with in relating to Hibernate:

The mapping of queries looks best done to a Criteria query, with .eq Restrictions for non-null template values.

Currently element-type handling is limited to simple types.

Update

This is implicitly detected by the session, but explicitly communicated to the dataspace as an object change. The issues are:

Case sensitivity

Normally Java string comparison is case sensitive (binary), and so equality constraints and order are normally also case sensitive. However some databases may have case insensitive collation. Ideally the underlying database should be configured with case-sensitive collation (see configuration for mysql, above). Otherwise an additional layer of query result filtering would be required to ensure that only correct (case-insensitive matched) values are returned. If used with setFirstResult/setMaxResults this would prevent  the mapping of these through to Hibernate, because the number of truly matching results would not be unknown until the results were checked. Similarly, the orders of results resulting from addOrder would differ.

Known issues

As noted above, currently:
In addition:

Change Log

2007-10-24
2006-07-17
2006-05-08
2006-04-26
2006-04-05