Serializing objects to HSQLDB using blobs
Our task is to persist a state object called State in a database. State object has globally unique identifier and some other properties. And is serialisable.
Let’s start with creating methods for serialisation/deserialisation:
protected byte[] serialise(T state) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(state);
oos.flush();
return bos.toByteArray();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
protected T deserialise(byte[] byteArray) {
try {
ObjectInputStream oip = new ObjectInputStream(new ByteArrayInputStream(byteArray));
return (T) oip.readObject();
} catch (IOException e) {
throw new IllegalArgumentException(e);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
So, we know how to serialise and deserialise object. The next step would be to think where to store that data. I have chosen HSQLDB for that example but it should be very easy to swap to some other db. It should be as easy as changing datasource properties and maybe amend a bit create script. Let’s crate one then for HSQLDB:
create table State ( object_id VARCHAR(32) not null primary key, object_value LONGVARBINARY );
Very simple table: object_id which a primary key and objet_value which is a binary representation of the object. And there is beauty of different flavours of databases (not!). We want to store the binary representation in a blob, and for HSQLDB there is no data type as blob. We have to use LONGVARBINARY. We have to be careful when switching to some other vendor and amend the create script. I will skip the part with details how to create schema etc. Contact me if you want more details on that.
Let’s create a datasource for HSQLDB in memory now using org.apache.commons.dbcp.BasicDataSource:
String driver = "org.hsqldb.jdbcDriver";
String url = "jdbc:hsqldb:mem:testdb";
String username = "sa";
String password = "";
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
The datasource will be used by Spring JdbcTemplate (org.springframework.jdbc.core.JdbcTemplate) which we will happily use for interactions with db. You can reuse this object:
JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
Let’s have a look how to store a serialised object in database (add necessary validation/exception handling):
public void add(T state) {
Object[] params = new Object[] {state.getId(), new SqlLobValue(serialise(state))};
int[] types = new int[] { Types.VARCHAR, Types.BLOB };
jdbcTemplate.update("insert into State (object_id, object_value) values(?, ?)", params, types);
}
Retrieving of that object is slightly more complex. We have to create a callback RowMapper (org.springframework.jdbc.core.RowMapper) first which will map each row from database to our object. This object can be as well reused:
private RowMapper rowMapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
byte[] bytes = rs.getBytes("object_value");
return deserialise(bytes) ;
}
};
And finally the code for retrieving and deserialising the object from database (validation and exception handling removed):
public T get(String id) {
Object[] params = new Object[] {id};
int[] types = new int[] {Types.VARCHAR};
List results = jdbcTemplate.query("select object_id, object_value from State where object_id=?", params, types, rowMapper);
if (results.size() == 0)
return null;
return (T)results.get(0);
}
Popularity: 38% [?]
