Getting started

Download and installation

Unfortunately, there are no downloads available yet.

After an imaginary download, you would have opencabinet.jar which you can add to the classpath of your Java program, and a simple command-line utility to create the database tables that OpenCabinet needs.

The OpenCabinet data types

OpenCabinet offers a couple of Java classes (mostly collection types) that you have to use when you want to store data in OpenCabinet. Each of these classes maps to a database table.

Continuing the bug database example, let us see how OpenCabinet's data types can be used.

FieldTypeRemarks
bug_idintThe bug ID
bug_severityvarchar(64)The bug severity level
bug_statusvarchar(64)The bug's status
product_idintForeign key into products table
assigned_to?The current owners of the bug (user account numbers)
reporterintThe user who reported this (user account number)
creation_tsdatetimeThe time of the bug's creation.
descriptiontextThe bug report and follow-up discussions
original TBL_BUGS

IntegerSet

For assigned_to we want to store a set of account numbers. We will use the OpenCabinet collection type IntegerSet, which implements the Java Set<Integer> interface.

IdString

While we are at it, let us also replace the varchar fields bug_severity, bug_status, and the numeric product_id with IdStrings.

An IdString is a short (up to 400 characters), non-empty string type that is especially userful for keys or ids that come from a limited set of options.

all OpenCabinet data types are referenced by integers

The contents of these data types are stored in separate tables that are being managed by OpenCabinet. To refer to them, every piece of data is assigned an integer id, which you can then store in your original database table.

FieldTypeRemarks
bug_idintThe bug ID
bug_severityint (IdString)The bug severity level
bug_statusint (IdString)The bug's status
product_idint (IdString)Foreign key into products table
assigned_toint (IntegerSet)The current owners of the bug (user account numbers)
reporterintThe user who reported this (user account number)
creation_tsdatetimeThe time of the bug's creation.
descriptiontextThe bug report and follow-up discussions
TBL_BUGS using IntegerSet and IdString

The data associated with a given integer id never changes. Furthermore, if you save the same data twice, you will get the same id (and the data will actually be only stored once).

Inserting into the database

QueryRunner q = new QueryRunner(dataSource);

In this (and all other code examples), we will use the popular Apache Commons DbUtils library instead of the raw JDBC API, because raw JDBC is too painful to write and look at. You can use OpenCabinet without DbUtils, though.

// without cache
CabinetManager cabMan = new DatabaseCabinetManager(dataSource);

All interaction with OpenCabinet is done via a CabinetManager. The example above creates one using a JDBC DataSource.

// with cache
CabinetManager cabMan = new DatabaseCabinetManager(dataSource, 100);

Since the data in OpenCabinet never changes, it lends itself to caching. The second parameter in the constructor above creates an in-memory for the most recently used 100 instances of each data type. This should significantly reduce repeated database access.

int bugId = 1;
int reporter = 34567;
Timestamp ts = new Timestamp(System.currentTimeMillis());
String description = "it just does not work";

IdString severity = new IdString("CRITICAL");
IdString status = new IdString("OPEN");
IdString product = new IdString("VaporWare Enterprise Edition");
IntegerSet assignedTo = new IntegerSet(12345, 23456);

This creates instances of OpenCabinet data. They are not yet stored in the database, though (or maybe they are, from a previous session, but we cannot be sure about that).

q.update("insert into tbl_bugs values(?,?,?,?,?,?,?,?)", 
  cabMan.persistAll(
     bugId, severity, status, product, assignedTo, reporter, ts, description ));

We can use the CabinetManager to persist these data in OpenCabinet's internal tables, and then use the the integer id that are thereby created to insert into the bug database table.

The convenience method persistAll takes a variable number of arguments, and returns an Object[] with the same number of elements. For every argument that is an OpenCabinet persistable data type, that data is stored and its id is placed in the output array. Any other kind of argument is simple passed through. This provides for a simple way to create the bind array for the insert statement.

Retrieving from the database

Object[] row = (Object[]) q.query(
                "select * from tbl_bugs where bug_id = ?", bugId,
                new ArrayHandler());

IntegerSet a = cabMan.get(IntegerSet.class, ((Number)row[4]).intValue());

In order to retrieve OpenCabinet data from the database, you have to go the other way round: turn the integer id into a Java instance.

// or all at once

cabMan.getAll(row, null, IdString.class, IdString.class,
                IdString.class, IntegerSet.class, null, null, null);

// now row contains IdStrings and the IntegerSet

Again, there is a convenience method getAll that turns a whole row of data (in the form of an Object[]) into instances. The arguments after the row specify what kind of data this should be. You can pass in null for unrelated data .

Searching the database

Searching the bug database using fields containing OpenCabinet data involves establishing the integer ids for data instances that match the search criteria, and then using these integer in the WHERE clause of the SELECT statement against TBL_BUGS.

Searching for exact matches

The easiest search pattern you can do are queries for exact matches. In this case, you can ask OpenCabinet for the unique id for the piece of data to match, and use that id directly in your SQL:

 // find all bugs for a given product
 Integer id = cabMan.getId(product);
 List<Object[]> result =
   (id != null)    // the product name could be invalid, then the id is null
      ? q.query("select * from tbl_bugs where product_id = ?", id, new ArrayListHandler())
      : new ArrayList<Object[]>();

Using the Query interface for more complex searches

Instead of manually retrieving the id, you can also use the OpenCabinet Query interface. The OpenCabinet data types (like IdString or IntegerSet) define a number of find methods that are used to specify search criteria.

  // find all bugs for a given product
  Set<Integer> productIds = cabMan.findIds(product.findExactMatch());
  
  // find all bugs assigned to user 12345
  Set<Integer> assignedIds = cabMan.findIds(new IntegerSet(12345).findSuperSets())

This way, you will end up with a number of id sets that you can use to match against product_id and assigned_to. Assuming that TBL_BUGS is in the same database as the OpenCabinet tables, you can avoid both these potentially large intermediate results and the need to manually bind them in your WHERE clause by asking OpenCabinet to create sub-select queries instead of materializing the search results.

  // find all bugs for a given product assigned to user 12345
  DatabaseCabinetManager cab = (DatabaseCabinetManager) cabMan;
  SingleTableSelect select = new SingleTableSelect("*", "tbl_bugs");
  select.addWhereClause(cab.makeSQLWhereClause("product_id", product.findExactMatch()));
  select.addWhereClause(cab.makeSQLWhereClause("assigned_to", new IntegerSet(12345).findSupersets()));
 
  List<Object[]> result = q.query(select.getSQL(), select.getBinds(), new ArrayListHandler());