Published November 2002
The move away from client-server and towards multi-tier architectures has tended to separate business logic from its data. Keeping data close to where it is accessed, by means of caching, is a well known solution to performance and scalability problems but the use of highly productive generic component frameworks, such as J2EE, has reduced the ability of developers to cache data without competing with the spirit of these component technologies.
livestore takes a unique approach to caching that enables J2EE component developers to write J2EE compliant components, such as Entity Beans (CMP or BMP), Session Beans, Servlets or Java Server Pages completely independently from livestore. livestore let's you keep your components pure in that they only use standard J2EE APIs. This allows them to be reused in non- livestore situations thus avoiding the vendor lock-in that has become so common with other products. Once components are functionally complete and portable, livestore can then be configured into the system to improve their performance and scalability.
In order to provide this completely seamless caching facility to all forms of components that may wish to access enterprise data, livestore exposes itself using the only data access API that is part of the J2EE specification: Java DataBase Connectivity - JDBC.
Enterprise scale applications often require multiple servers to be clustered together. livestore provides a synchronisation facility that enables clusters of livestore enabled applications to keep a consistent and up-to-date cache of dynamic business data.
Adding livestore caching to an application is a simple four-step process, which will normally require no changes to your application code. In this article, we will walk through a simple example. The application is a simple, standalone payroll application that has two tables, named EMPLOYEE and DEPARTMENT. The EMPLOYEE table has columns ID, FORENAME, SURNAME, DEPARTMENT and SALARY. The DEPARTMENT table has columns ID and NAME.
To use livestore with this simple database, we need to go through the following step.
livestore requires an additional, integer column on each table that you want to be cached. This column is used by livestore for optimistic concurrency control. It is managed automatically by livestore and your application need never know about it. In this case, we add a column named VERSION to each table.
livestore also requires that each table has a primary key constraint, but you will usually already have these in place. In this example, the primary key in both tables is the column named ID.
If you are using an application server, you will need to do this by modifying your server startup scripts, or changing its configuration as described in its documentation. If your application is stand-alone, you can do it my modifying the CLASSPATH environment variable, or the command line used to start your application. In the example, the application is standalone, so we modify the CLASSPATH environment variable to include livestore.jar.
The easiest way to do this is to use the configuration generator supplied with livestore. You can find it in the bin directory of the installation, and run it like this:
config-gen.bat c:\payroll\livestore-config.xml -username payroll -password payroll -driverClass oracle.jdbc.driver.OracleDriver -diverUrl jdbc:oracle:thin:@payroll:1521:payroll -ocCol VERSION -schemaPattern PAYROLL -maximumCapacity 10
The configuration generator reads the database metadata, checks that it is suitable for use with livestore, and generates a configuration file to the supplied filename. The first four arguments should be self-explanatory, as they are common to many JDBC-based applications. The -ocCol argument is just the name of the optimistic control column we added to the schema in step one. The -schemaPattern argument lets you define the part of the database to output configuration for (there is also a -catalog argument). The -maximumCapacity argument lets you set the maximum number of database connections the application will use. Initially, you should set this to the size of the connection pool you have been using without livestore, but using livestore will enable you to reduce it later.
Most J2EE applications read their configuration information either from an external file, or fetch it from an application server. This configuration information tells them which JDBC driver to use. In the case of our payroll application, we simply modify the file to replace mentions of the JDBC driver class name with com.isocra.livestore.api.factories.Driver, and the database URL with jdbc:livestore:file:\c:\payroll\livestore-config.xml.
Once these steps have been performed, the next time the application is run, all database queries will be routed via livestore instead of via the original JDBC driver.
The very small amount of configuration we have done is already enough to give the payroll application a significant performance boost. With the configuration we have generated automatically, livestore will already be able to cache the responses to any query using the primary key of any cached table. This accounts for a great many of the queries performed by typical J2EE applications.
In our payroll example, there is a facility to display all the information about a particular employee. This is done using a query like this:
SELECT FORENAME, SURNAME, SALARY, DEPARTMENT FROM EMPLOYEE WHERE ID = 235
The first time livestore sees this query, it will send it to the database, and cache the result. If the application performs the same query again, however, the answer will come from livestore's cache. A query response retrieved from cache reaches your application orders of magnitude faster than if it had been retrieved from the database.
livestore can also keep its cache up-to-date with changes made by the application. If, after the query above, the application were to modify the employee's salary with an update like this:
UPDATE EMPLOYEE SET SALARY = 100000 WHERE ID = 235
livestore will not evict the row representing employee number 235 from the cache, but instead, it will update it to reflect the change as well as writing the update through to the database itself.
Even though livestore can produce substantial performance improvements on primary key queries alone, it is possible to make use of its cache for other queries too. For example, you may well want to find all the employees in a department with a query like this:
SELECT ID FROM EMPLOYEE WHERE EMPLOYEE.DEPARTMENT = 20
With the automatically generated configuration, it will not be possible to answer this query from cache unless the cache already contains all the employees, which is unlikely to happen very often.
However, by adding a single line to the configuration file, you can add an index to the livestore cache as follows:
<Index columns="DEPARTMENT" cache="true" unique="false"/>
With this entity added to the configuration file, livestore will cache the results of queries using the DEPARTMENT column, so if the query above was performed twice, the second time the result would come from the cache.
You can download an evaluation copy of livestore from http://www.isocra.com/products/livestoreregister.php, or you can find out more about livestore at http://www.isocra.com/products/.
Isocra was founded in 1998 in a response to the need for a greater emphasis on design excellence and sound engineering principles in leading-edge technologies. Isocra specialises in technologies such as e-commerce, distributed objects and enterprise software. The company is based in Cambridge, UK. To find out more about isocra see the website at http://www.isocra.com.