I ended up using a hybrid opproach of hsql and castor. hsql for the archival data, and castor for the user data.
One thing I thought would be worth sharing is a simple view for querying the embeded hsql database, this is great for development mode. Note, it tis not meant to be include in a finished product (sql in the view, yuck)
See attached screen-shot:
Code:
public class SqlNavigatorView extends AbstractViewImpl implements ApplicationListener
{
private DataSource dataSource;
private JTextArea sql = new JTextArea();
private JTextArea sqlUpdate = new JTextArea();
private JTable table;
/**
* @see org.springframework.richclient.factory.AbstractControlFactory#createControl()
*/
protected JComponent _createControl()
{
JTabbedPane tabs = new JTabbedPane();
GridBagLayoutBuilder builder = new GridBagLayoutBuilder();
builder.append(new JScrollPane(sql), 1, 2, 1.0, 1.0);
builder.append(execute.createButton(), 1, 1, 0.0, 0.0);
builder.nextLine();
builder.append(Box.createVerticalGlue(), 1, 1, 0.0, 1.0);
tabs.add("Select", builder.getPanel());
builder = new GridBagLayoutBuilder();
builder.append(new JScrollPane(sqlUpdate), 1, 2, 1.0, 1.0);
builder.append(executeUpdate.createButton(), 1, 1, 0.0, 0.0);
builder.nextLine();
builder.append(Box.createVerticalGlue(), 1, 1, 0.0, 1.0);
tabs.add("Update", builder.getPanel());
JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
split.setTopComponent(tabs);
split.setBottomComponent(new JScrollPane(table = new JTable()));
return split;
}
protected ActionCommand execute = new ActionCommand("runSql", "Run")
{
protected void doExecuteCommand()
{
final StatusBar bar = (StatusBar) getActiveWindow().getStatusBar().getControl();
bar.setCancelEnabled(false);
bar.taskStarted("Executing Statement...", StatusBar.UNKNOWN);
final SwingWorker worker = new SwingWorker()
{
public Object construct()
{
getActiveWindow().getControl().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Connection connection = null;
PreparedStatement statement = null;
ResultSet result = null;
try
{
connection = dataSource.getConnection();
statement = connection.prepareStatement(sql.getText());
result = statement.executeQuery();
SqlTableModel model = new SqlTableModel(result.getMetaData());
table.setModel(model);
model.fireTableStructureChanged();
while (result.next())
{
Object[] values = new Object[model.getColumnCount()];
for (int i = 0; i < model.getColumnCount(); i++)
{
values[i] = result.getObject(i + 1);
}
model.getData().add(values);
}
model.fireTableDataChanged();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
result.close();
statement.close();
connection.close();
}
catch (SQLException ignore)
{}
}
return null;
}
public void finished()
{
bar.done();
getActiveWindow().getControl().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
};
worker.start();
}
};
protected ActionCommand executeUpdate = new ActionCommand("runSql", "Run")
{
protected void doExecuteCommand()
{
final StatusBar bar = (StatusBar) getActiveWindow().getStatusBar().getControl();
final SwingWorker worker = new SwingWorker()
{
public Object construct()
{
getActiveWindow().getControl().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Connection connection = null;
Statement statement = null;
ResultSet result = null;
try
{
connection = dataSource.getConnection();
statement = connection.createStatement();
int count = statement.executeUpdate(sqlUpdate.getText());
bar.setMessage("Updated " + count + " rows.");
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
statement.close();
connection.close();
}
catch (SQLException ignore)
{}
}
return null;
}
public void finished()
{
getActiveWindow().getControl().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
};
worker.start();
}
};
/**
* @param dataSource The dataSource to set.
*/
public void setDataSource(DataSource dataSource)
{
this.dataSource = dataSource;
}
private class SqlTableModel extends AbstractTableModel
{
private int columnCount;
private String[] columnNames;
private List data = new ArrayList();
public SqlTableModel(ResultSetMetaData metaData) throws SQLException
{
columnCount = metaData.getColumnCount();
columnNames = new String[columnCount];
for (int i = 0; i < columnCount; i++)
{
columnNames[i] = metaData.getColumnLabel(i + 1);
}
}
public String getColumnName(int column)
{
return columnNames[column];
}
public int getColumnCount()
{
return columnCount;
}
public int getRowCount()
{
return data.size();
}
public Object getValueAt(int row, int column)
{
return ((Object[]) data.get(row))[column];
}
public List getData()
{
return data;
}
}
}