iBatis ORM and Caching Strategy - a Use Case

From:
"andrew cooke" <andrew@...>

Date:
Sat, 27 Sep 2008 04:34:37 -0400 (CLT)

The Application
I am writing a Java application that presents information, stored in a
database, via a web server. The presentation does not modify the data,
but the database does evolve, slowly, on disk (modified by other
processes).
The data themselves can be divided into two groups: first a set of related
objects that describe the system being measured (in this context they
could be considered "metadata"); second a set of measurements. There are
many more measurements than there are metadata.
Some iBatis Details
iBatis provides very simple caching at the SQL statement level. So if a
query is repeated with the same parameters then the result may be
retrieved from the cache. The advantage of this approach is that it is
simple to implement and understand. The disadvantage is that it is not
"intelligent", in that it does not recognise object identities.
For example, if I have two different queries that both return an instance
of the same class, iBatis will cache two different instances of the same
object for each key - one from each query.
iBatis is also limited in how it will map graphs - it does not provide
transparent retrieval of related objects. So if one class references
another class then either both must be mapped in a single query to iBatis,
or a second call must be made explicitly (in which case the first instance
presumably contains a key that will be used to find the correct value).
This limitation can be removed by using a second layer - for example
Spring AOP.
My Solution
I have decided to use a very simple mapping approach with two levels of
cache. The first level of cache is the iBatis statement cache; the second
is a set of maps from keys to instances for the "metadata" objects. This
second level holds all metadata instances in-memory. The caches are in a
singleton "database interface" class.
The connections between related objects are not explicit in the objects
themselves. Instead, the "database interface" provides methods that
retrieve related objects (for example "List<Foo> getFoosForBaz(Baz)").
One drawback to this approach is that it is not very "OO". The instances
do not form a graph that can be traversed directly. For this particular
application that is not an issue - there is very little "analysis" of the
data (it is just "dumb presentation").
Another drawback is that for each metadata class the ORM manages two
different types - instances and keys. iBatis must return instances when
asked for "all instances" to populate the cache, but must return only keys
when asked about relations between objects (the keys are then used to
retrieve instances from the second level cache). In practice, with some
basic engineering (return factory objects rather than keys; these take the
"database interface" and extract the cached value), the extra work is
minimal.
The advantages are that the scheme is fast, compact, and relatively easy
to understand. It is fast because almost all information is cached:
metadata instances are cached in the second level caches; relationships
are cached (as lists of keys) in the iBatis cache. It is compact because
no duplicate instances are created (except when a cache expires).
The simplicity is not so obvious until you start to consider details like
cache expiry. Because there are no explicit relations each pool of
instances (caches are grouped by class) can expire independently. There
is no worry about instances being "trapped" in complex graphs. There
should be no memory leaks.
This approach is very different to how I used SQLAlchemy in Python. The
approach I used there was much more "sophisticated", with transparent,
lazy retrieval of related objects. In that case I was writing a client
application that did not need to be long-lived, reliable, or efficient
(within reason, of course).
It's possible that I am being too conservative in this case. Spring AOP
(or Hibernate) might have made the code more transparent at no extra cost
in practice. But I think this was a reasonable approach to use here,
given the circumstances (both the application - particularly the "read
only" nature - and my limited knowledge).
Andrew

More iBatis Comments

From:
"andrew cooke" <andrew@...>

Date:
Sat, 27 Sep 2008 09:01:17 -0400 (CLT)

When I was first deciding which ORM solution to use for this project I
read, somewhere, a comment suggesting that Hibernate and iBatis were both
good products, and that the choice of which to use depended on how you
wanted to think about the database: Hibernate was best for those who saw
the database as a transparent store for Java code; iBatis was better for
those who wanted to explicitly manage an interface between Java and SQL.
I don't know if that is true, but one more advantage of the approach
described here is that there's no real restriction to follow a "data
model". Last night I was working through some uses cases for the user
interface and realised that, for "usability", I needed to present a
relationship between the metadata objects that was completely unintuitive
- it would not have been present in a traditional graph of objects and
will require some custom SQL to generate. That's no problem here.
(You could argue that I simply has a bad design. I would say that the
intuitive / straw-man data model above is actually a good design and that
this relationship is really an alternative view of the system that is
particularly useful in one context, So in a more "OO" approach it would
be better considered the result of some analysis by the system (and still
not directly expressed in the data model).
The difference then comes down to how to manage that analysis in an
efficient manner; with the approach outlined here I can place the analysis
in the database and rely on the existing caches. If I had been working
with transparently mapped objects I would have been tempted to do the
analysis in Java and add an explicit cache. From my point of view (ie
preferring to use SQL to do a complex query rather than write Java code)
the approach here is simpler.)
Another possible advantage is the clear separation of concerns. Quite
naturally my implementation has a single, well-defined interface that
describes all the information I retrieve from the database. A requirement
that I didn't mention earlier is that the system be easy to move to a
in-house (client) data storage system that is not based on SQL. Again: no
problem here.
Finally, given the somewhat "crude" (or "hands on") approach I have
described, you may ask whether iBatis is "worth it". It most certainly
is. First, it does most of the work of constructing objects for me
(although there is often a final pass to explicitly retrieve objects from
the second level cache). Second, it allows easy separation of SQL and
Java (in separate files). Third, it provides a mechanism for adapting the
SQL to different databases (I pass the engine name as a parameter in
queries and that can be used by iBatis to construct the correct query).
Fourth, it provides the useful first level cache.
Andrew

Simplified Caching; Problem with iBatis, Spring and OSCache

From:
"andrew cooke" <andrew@...>

Date:
Sat, 27 Sep 2008 20:36:36 -0400 (CLT)

After writing the above and sleeping on it, I realised that I had made
things unnecessarily complicated. A single cache (provided by iBatis) is
sufficient. The only modifications needed are:
- Instead of caching all instances in a second level cache, we
retrieve instances explicitly via their keys, one by one. This
places each instance in the iBatis cache.
- Any query that references an instance returns the key, wrapped in
a factory as before. The factory is then passed the database
interface and requests the instance from the cache.
- The factories are themselves cached by iBatis. So they can be
made mutable, storing a weak reference to the instance that was
retrieved. If they are then re-used they do not need to repeat
the lookup (the factory "short circuits" via the stored value).
That required a couple of hours to implement and works fine.
I then decided to investigate how teh caching was working. To do this I
switched to OSCache and monitored the cache administrator via JMX - see
http://www.opensymphony.com/oscache/wiki/JMX Monitoring.html
That would have worked perfectly, except that iBaits will not use the
OSCache created in Spring. It always creates its own instance. After
looking in detail at the iBatis docs (the dev API at
http://ibatis.apache.org/docs/java/dev/index.html ) I realised that this
is a consequence of the caching architecture - the CacheModel creates a
CacheController instance via the constructor. There is no way to inject a
value!
This is a serious design flaw for iBatis. It means that caching cannot be
integrated with Spring (there's not even any way to programmatically
retrieve the cache that I can see).
Andrew

More Info on IBatis-Based Project

From:
"andrew cooke" <andrew@...>

Date:
Thu, 16 Oct 2008 09:14:37 -0300 (CLST)

I wrote a paper summarising the architecture of the project, including
iBatis. It's here - http://www.acooke.org/kpi.pdf - and might be useful
as background for the comments above (or as more detail about how I used
iBatis within a J2EE web application).
Andrew

iBATIS Caching

From:
"andrew cooke" <andrew@...>

Date:
Sun, 16 Nov 2008 16:00:15 -0300 (CLST)

I was going to add a post summarizing this, but it's simpler to just
include the whole email. Thanks, Clinton.
---------------------------- Original Message ----------------------------
Subject: iBATIS Caching
From: "Clinton Begin" <clinton.begin@...>
Date: Tue, November 4, 2008 12:34 am
To: andrew@...
--------------------------------------------------------------------------
Hi Andrew,
A friend of mine forwarded a post on to me and I thought I might offer some
advice...
If you'd like iBATIS to use the Spring instance of OSCache, you can create
your own implementation of CacheController. The interface is quite simple
and self explanatory (but is documented in the javadocs a little more, as
well as the user guide, wiki and our book).
Your cache controller implementation can request the instance of OSCache
from Spring's app context. It should be quite simple. To use your
implementation, you can configure it like any other cache.
<cacheModel id="someCache" type="org.acooke.some.app.cache.CustomOSCache"
readOnly="true" serialize="false">
<flushInterval hours="24"/>
<flushOnExecute statement="updateAccountViaInlineParameters"/>
<property name="size" value="1"/>
</cacheModel>
Any <property> elements will be passed to setProperties after
instantiation. You can use a typeAlias to shorten the type attribute if
you're going to use it more than once.
public interface CacheController {
public void flush(CacheModel cacheModel);
public Object getObject(CacheModel cacheModel, Object key);
public Object removeObject(CacheModel cacheModel, Object key);
public void putObject(CacheModel cacheModel, Object key, Object object);
public void setProperties(Properties props);
}
Hope that helps,
Clinton