Wednesday, August 09, 2006

Crossing generics and covariant returns

Quick: why doesn't this compile?

fred.setFoo(new Foo());

Ok, some context:

class Bob<Foo extends Foo> {
    private Foo foo;

    public void setFoo(final Foo foo) {
        this.foo = foo;
    }
}

class Fred extends Bob<Bar> {
}

See it now? How about now:

class Foo {
}

class Bar extends Foo {
}

class Bob<NOTFOO extends Foo> {
    private NOTFOO foo;

    public void setFoo(final NOTFOO foo) {
        this.foo = foo;
    }
}

class Fred extends Bob<Bar> {
}

Yes, it was a mean trick to name a generic parameter the same as the class it extends. Sensibly, SUN's javac complains "illegal forward reference". I had to reread several times the code this example came from before it finally clicked what had happened.

But enough; on to the meat of the post.

I was cleaning some code and noticed this mini-anti-pattern in many places:

class Bob {
    private Foo foo;

    public Foo getFoo() {
        return foo;
    }

    public void setFoo(final Foo foo) {
        this.foo = foo;
    }
}

class Fred extends Bob {
}

class SomewhereElse {
    public void doWork(final Fred fred) {
        final Bar bar = (Bar) fred.getFoo();
    }
}

This style is quite commmon in pre-generics Java where there are parallel inheritance hierarchies: Foo goes with Bob, Bar goes with Fred, et al, and all the common code is pushed down into the lowest base class. All that casting makes my eyes sore.

But using generics, I get the advantages of covariant returns without needing any extra coding:

class Bob<F extends Foo> {
    private F foo;

    public F getFoo() {
        return foo;
    }

    public void setFoo(final F foo) {
        this.foo = foo;
    }
}

class Fred extends Bob<Bar> {
}

Now the syntax error I first posed is a real time-saver: instead of getting a ClassCastException at runtime, I get a compile error when trying:

fred.setFoo(new Foo());

The correct call is now:

fred.setFoo(new Bar());

What are covariant returns? To get generic code like this to work right, Java picked up covariant returns for free:

class abstract NumberPicker {
    public abstract Number pickANumber();
}

class IntegerPicker extends Base {
    public Integer pickANumber() {
        return 42;
    }
}

Because an Integer is a Number, Java recognizes that declaring IntegerPicker to return an Integer for pickANumber() satisfies the contract for NumberPicker: covariant returns. Generics uses the same trick in the compile when narrowing the return type for getFoo() in the first examples, or something near enough.

You can read more on this and other goodies in Angelika Langer's Java Generics FAQs.

Tuesday, August 08, 2006

SQL and XML and JDBC4 and you

I missed it when he first posted: Lance Anderson (lead for JDBC4) has an excellent post with short recipes on using XML and SQL with JDBC4, JDBC 4.0 SQLXML Interface. Just the sort of thing when one is poking around for an easy way to use the new XML features of JDBC4.

Friday, August 04, 2006

A refinement on constructor tidiness

Last year I wrote a post about using generics to improve constructor argument checking, Let generics make your Java constructor cleaner. Since then I have refined the example method, asNotNull.

The original:

public static <T> T asNotNull(final T input) {
    if (input == null)
        throw new NullPointerException();

    return parameter;
}

And updated:

public static <T> T asNotNull(
        final T input, final String message) {
    if (null == input)
        throw new IllegalArgumentException(message);

    return input;
}

There are two differences:

  1. I throw IllegalArgumentException instead of NullPointerException. In addition to this being arguably more correct for the semantics of those two exceptions, it is also more useful as I will know that any NullPointerException is because of a call on a null object.
  2. I use a message for the thrown exception—usually of the form "Missing variableName"—to aid in fixing the problem from the stack trace output.

Another reason for the changes are consistency. I make use of other "contract"-based methods such as asNotEmpty, etc., who all throw IllegalArgumentException with an explanatory message. The consistency makes it easier for other programmers to take up the methods into their own code.

Thursday, August 03, 2006

Wednesday, August 02, 2006

Doug Lea's list of Mustang concurrency changes

Doug Lea of java.util.concurrent fame maintains the Concurrency JSR-166 Interest Site. One of the interesting links is to Sun bug activity related to JSR166, Concurrency or Collections.

Lea's bug list is very handy for watching for interesting Mustang changes in concurrency or collections as they happen. I only stumbled across the list today—thank Google—, and noticed the nifty Add @see newSetFromMap for Map implementations without Set implementations fix, which got me to look at java.util.Collections again and check the new methods.

Serendipity strikes.

Javadoc generic type parameters

While looking at the new NavigableSet interface introduced into JDK 6 (Mustang), I noticed this in the class javadocs:

 * @param <E> the type of elements maintained by this set
 * @since 1.6
 */
public interface NavigableSet<E> extends SortedSet<E> {

Well, that is new. I've not seen a generic type parameter used with the @param tag before.

Looking at the documentation for Javadoc 1.5, apparently this feature has been around since generics started.

A quick comparison between the sources of java.util.Set for JDK 5 and JDK 6 shows that Sun has added @param tags to the generic collection classes in Mustang.

Unfortunately, IntelliJ IDEA 6.0 beta does not show these class generic tags in the class quickdocs, which is why I never noticed them before. Hopefully JetBrains fixes this at some point.

On the flip side, the JDK 6 javadoc tool does handle the tags. Witness java.util.Set for Mustang.

Tuesday, August 01, 2006

Java generics trick to work out parameter types

Another nifty trick I hadn't seen before which I ran across on the Hibernate page for their generic DAO classes:

public abstract class GenericHibernateDAO<T, ID extends Serializable>
        implements GenericDAO<T, ID> {
    private Class<T> persistentClass;
    private Session session;

    public GenericHibernateDAO() {
        this.persistentClass = (Class<T>)
                ((ParameterizedType) getClass()
                .getGenericSuperclass())
                .getActualTypeArguments()[0];
    }

    // ...
}

Playing around with this revealed two things to me:

  1. This trick only works for concrete subclasses.
  2. The concrete subclass needs to not be generic, at least for the parameter in the interesting expression above.

So one could, for example, create a generic factory using this technique, although there does not seem on the surface to be much advantage:

public abstract class FactoryBase<T> {
    private final Class<T> type = (Class)
            ((ParameterizedType) getClass()
            .getGenericSuperclass())
            .getActualTypeArguments()[0];

    protected FactoryBase() { }

    public Class<T> getType() { return type; }

    public T create()
            throws IllegalAccessException, InstantiationException {
        // Real implementations do something more interesting
        // than imitate "new T()".
        return type.newInstance();
    }
}

public class FredFactory
        extends FactoryBase<Fred> { }

public class Main {
    public static void main(final String arguments) {
        final Fred fred = new FredFactory().create();
    }
}

How different is this from supplying a Class literal to the factory consturctor?

getGenericSuperclass() seems one of those "back pocket" techniques: something I'll keep handy if the need arises but which I don't go out of my way to use.

UPDATE: A little playing shows that the minimal requirement is that the generic parameter whose index is queried with getActualTypeArguments() must be concrete; it is ok for another parameter to be a generic:

public class Bob<T, U> {
    final Class<T> tClass
            = getGenericTypeByIndex(getClass(), 0);

    private static Class getGenericTypeByIndex(
            final Class clazz, final int index) {
        return (Class) ((ParameterizedType) clazz
                .getGenericSuperClass())
                .getActualTypeArguments()[index];
    }
}
}

This snippet works ok as:

public class Fred<U> extends Bob<Fred, U> {
    public static void main(final String arguments) {
        new Fred<String>();
    }
}

But fails at runtime with a ClassCastException trying to turn a sun.reflect.generics.reflectiveObjects.TypeVariableImpl into a java.lang.Class:

public class Sally<T, U> extends Bob<T, U> {
    public static void main(final String arguments) {
        new Sally<Sally, String>();
    }
}

Monday, July 31, 2006

Peter Norvig's Infrequently Answered Questions

Here's a Java resource I wish I had known about long ago: Peter Norvig's Java IAQ.

UPDATE: Thanks to anonymous for pointing out I mispasted in the link!

Saturday, July 29, 2006

A Java trick with varargs to enforce argument count

Perhaps you've seen this before, but it is new to me:

public void doSomethingAmazing(
        final String firstArgument,
        final String... restOfArguments) {
    // Do something amazing with the arguments
}

What is the point of firstOne and restOfThem (besides looking a little LISPy)?

Fortunately, the coder had this comment for the method: Change from checking at runtime for one or more arguments to syntatically enforcing a mandatory first argument and optional list of remaining arguments. A quick check showed the previous version as:

public void doSomethingAmazing(
        final String... arguments) {
    // Do something amazing with the arguments
}

Aha, how clever! Whereas before the call:

doSomethingAmazing();

compiled but at runtime threw an IllegalArgumentException for empty arguments[], now the same call is a syntax error caught by the compiler. The correct new call is:

doSomethingAmazing("wonderful");

I like it.

UPDATE: Paul Holser points out to me that he uses this trick in the excellent JAggregate. As I reviewed that code within the past several months, I now realize that it is most likely I had already seen this trick there first. Paul is a clever guy, and deserves the credit.

Wednesday, July 19, 2006

The double inheritance pattern in Java

Java is a single inheritance language, yes? Generics adds a new wrinkle to that, however. Consider this problem:

You have a class hierarchy you wish to wrap. For instance, you want wrapped versions of JTextComponent and its subclasses which implement additional functionality or default behaviors. So the first wrapping looks like this:

public class MyTextComponent
        extends JTextComponent {
    // ...
}

The second wrapping looks like this:

public class MyTextField
        extends JTextField or MyTextComponent {
    // ...
}

Oops! What should the second wrapper extend?

Generics provides a neat, if wordy, solution for this problem. Have the wrapper hierarchy extend only within itself, and have generic types extend the wrapped hierarchy. The wrapper hierarchy delegates to the wrapped one. Thus:

public class LabeledTextComponent<F extends JTextComponent>
        extends Box { // Box is just illustrative
    protected final F field;

    private final JLabel label;

    protected LabeledTextComponent(final F field,
            final String labelText) {
        super(BoxLayout.LINE_AXIS);

        this.field = field;
        label = new JLabel(labelText, SwingConstants.TRAILING);

        label.setLabelFor(field);
        
        add(label);
        add(field);
    }

    // ... public constructors

    // delegates via field specific to JTextComponent

    public String getText() {
        return field.getText();
    }

    public void getText(final String text) {
        field.setText(text);
    }

    // ... other delegates

    // methods specific to LabeledTextComponent

    public JLabel getLabel() {
        return label;
    }
}

public class LabeledTextField<F extends JTextField>
        extends LabeledTextComponent<F> {
    protected LabeledTextField(final F field,
            final String labelText) {
        super(field, labelText);
    }

    public LabeledTextField(final String labelText) {
        super(new JTextField(), labelText);
    }

    public LabeledTextField(final int columns,
            final String labelText) {
        super(new JTextField(columns), labelText);
    }

    // ... other public constructors, etc.
}

And a further example:

public class LabeledPasswordField<F extends JPasswordField>
        extends LabeledTextField<F> {
    protected LabeledTextField(final F field,
            final String labelText) {
        super(field, labelText);
    }

    // ... public constructors

    // delegates via field specific to JPasswordField

    public char[] getPassword() {
        return field.getPassword();
    }

    public void getPassword(final char[] password) {
        field.setPassword(password);
    }

    // ... other delegates
}

Generics combined with delegates simulates a kind of multiple inheritance similar to mixins. One can extend the idea one step further:

public interface Required {
    /** Checks if the field requires text to be valid. */
    boolean isRequired();

    /** Toggles that the field requires text to be valid. */
    void setRequired(final boolean required);

    /** Checks if the field is required and if the text is missing. */
    boolean isMissing();
}

public class RequiredTextField extends JTextField
        implements Required {
    private boolean required;

    // Required
    public boolean isRequired() {
        return required;
    }

    public void setRequired(final boolean required) {
        this.required = required;
    }

    public boolean isMissing() {
        return required
                && null != getText();
                && getText().length() > 0;
    }
}

public class RequiredLabeledTextField
        <F extends JTextField & Required>
        extends LabeledTextField<F>
        implements Required {
    protected RequiredLabeledTextField(final F field,
            final String labelText) {
        super(field, labelText);
    }

    // Required
    public boolean isRequired() {
        return field.isRequired();
    }

    public void setRequired(final boolean required) {
        field.setRequired(required);
    }

    public boolean isMissing() {
        return field.isMissing();
    }
}

Now it is simple to glue it all together cleanly, maintaining separation of concerns and working through interfaces based on capabilities rather than concrete classes.

Generics to the rescue!

NB — I relied heavily on my background in C++ templates for conceptualizing this pattern. But I believe it likely I am treading no new ground. If you know an earlier or better reference for this pattern, please drop me a line. I want to give credit to where it is due.

Friday, June 23, 2006

Skipping read-only text components in Swing with TAB

After a superficial web search, I did not turn up a simple way (read cut and paste) to TAB over read-only components while navigating a container in Swing. JComboBox, JTextComponent and JTree all have an isEditable/setEditable(boolean) pair, however it has different meaning for each of them.

The case I am interested in JTextComponent. For text components, uneditable is equivalent to read-only: you can select the text from the component but cannot change it. The alternative is to disable the component, but then the text is not selectable for copying.

So, given a uneditable text component, what happens when I traverse through a form with the TAB key? Unfortunately, the JDK by default will move the focus (the caret) into the read-only text field even though you cannot type anything. Fixing the form to skip over read-only text fields is straight-forward but not directly documented:

public static void skipReadOnlyTextComponents(
        final Container container) {
    // Without this, JDK ignores policy
    container.setFocusCycleRoot(true);
    container.setFocusTraversalPolicy(new MyPolicy());
}

class MyPolicy extends LayoutFocusTraversalPolicy {
    @Override
    protected boolean accept(final Component c) {
        return !isReadOnly(c) && super.accept(c);
    }

    private static boolean isReadOnly(
            final Component c) {
        if (c instanceof JTextComponent)
            return !((JTextComponent) c).isEditable();

        return false;
    }
}

In broader use, it is worthwhile to make an Editable interface for isEditable/setEditable and add that to the instanceof tests in isReadOnly.

Wednesday, June 21, 2006

Answer: Using JFormattedTextField for integers

I wrote earlier about using JFormattedTextField for integers and the difference between using an initial value of Integer (rounds input) v. BigInteger (rejects invalid input). I also wrote that I did not understand why there was a difference.

I now know what happened.

The culprit for handling Integer turns out to be NumberFormat and friends. The rounding behavior is not numerical rounding at all; it is truncating the input text.

The parser for the input stops after finding valid input and returns a parsed value in stringToValue(String):Object. This discards any trailing text, which in the case of parsing an integer is everything from the decimal point forward. The same behavior can be seen with a date parser: Jun 21, 2006xxx parses to Jun 21, 2006 and the xxx is discarded.

The solution to enforce correct input even with trailing garbage is cumbersome: initialize JFormattedTextField with a custom formatter and check that parsed input deparses back cleanly:

new JFormattedTextField(new Integer(0))

becomes:

new JFormattedTextField(new NumberFormatter(
        NumberFormat.getIntegerInstance()) {
    @Override
    public Object stringToValue(final String text)
            throws ParseException {
        // Throws if the input is corrupt from the start
        final Object parsed = super.stringToValue(text);
        final String deparsed = valueToString(parsed);

        // Throws if there is no clean roundtrip,
        // such as trailing garbage characters
        // For date parsing, etc., consider equalsIgnoreCase
        if (!deparsed.equals(text))
            throw new ParseException(text, deparsed.length());

        return parsed;
    }
}) {
    {
        // Initialize to the original starting value
        setValue(new Integer(0));
    }
}

(Yes, the anonymous instance syntax is awkward here.)

That's a lot of work to achieve what seems like a simple goal: actually valid input for a JFormattedTextField.

UPDATE: Given the hoops to jump through for this more "correct" solution, I think I'll stick to new JFormattedTextField(new BigInteger(0)) for now, although that does nothing to help with other input types such as dates.

Tuesday, June 20, 2006

Using JFormattedTextField for integers

I discovered the hard way that:

new JFormattedTextField(new Integer(0))

does not do what I wanted.

What I wanted was to have a text entry field restricted to integers. What I got was a field which rounded off numbers to integers. This was surprising.

After a while of poking at it, I tried using various incarnations of NumberFormat and DecimalFormat in the field constructor with no luck. Perusing the sources was not particularly illuminating either, at least at first.

Finally I noticed that JFormattedTextField.getDefaultFormatterFactory(Object) contained this legerdemain:

if (type instanceof DateFormat) { ... }
if (type instanceof NumberFormat) { ... }
if (type instanceof Format) { ... }
if (type instanceof Date) { ... }
if (type instanceof Number) { /* mucking around with formatter factory */ }

Hmmm. So in a flash of inspiration I tried:

new JFormattedTextField(new BigInteger(0))
and it worked, although I do not entirely understand why. JFormattedTextField maintains separately value, formatter and formatter factory in addition to the document model for text entry and a lot of state options for handling valid and invalid input.

I'm sure its a tour de force for someone at Sun, and most importantly the class works, but quirks like this one are difficult to find and explain.

UPDATE: A colleague of mine asked for more details on this point. To clarify:

Argument Input Behavior
new Integer(0) 3.0 3
3.3 3
new BigInteger(0) 3.0 3
3.3 invalid

So to get non-integral input to be treated as invalid, you must initialize JFormattedTextField with a BigInteger; initializing with an Integer gets a rounding mode instead.

Monday, June 19, 2006

Programmatically show a tool tip in Swing

A better way to do this exists, I am sure, but I failed to find it easily with Google: How to display a tool tip programmatically in Swing:

ToolTipManager.sharedInstance().mouseMoved(
        new MouseEvent(targetComponent, 0, 0, 0,
                0, 0, // X-Y of the mouse for the tool tip
                0, false));

Note the commented line. This is the relative location of the synthetic mouse event to the target component. The (0, 0) example above is as if the user had paused the mouse at the upper-left corner of the component, and the tool tip displays accordingly.

Another choice might be (targetComponent.getWidth(), targetComponent.getHeight()) for the lower-right corner, etc.

I wish Swing had a more straight-forward way of using the tool tip framework programmatically. I would like to be able to reuse tool tips for form validation. But at least this works.

UPDATE: 4 years on and I still get more positive comments on this post than any other. Hopefully the JDK improves in this area.

Monday, June 12, 2006

ViM 7

In another salvo in the endless vi v. Emacs war, the ViM folks released ViM 7.0.

The announcement is about a month old. Why the delay for me mentioning it? Cygwin just updated their 6.4 to 7.0 over the weekend. Too bad the update removed vi from /usr/bin. Oops.

UPDATE: I found one source of my Cygwin trouble. For whatever reason, the ViM 7.0 installation put vim.exe and friends into C:\cygwin\usr\bin instead of /usr/bin. As my default Cygwin installation mounts C:\cygwin\bin as /usr/bin, that means the mount hides the files in the underlying Windows directory:

C:\cygwin\bin -> /usr/bin
C:\cygwin\usr\bin -> hidden underneath

A quick unmount, switch to CMD.EXE, files moved to the right Windows directory, and remount: I can now run vim.

One remaining problem: whither the vi command?

UPDATE: Run the Cygwin installer/updater one more time. There is a patch up for the new ViM 7 package which fixes the missing /usr/bin/vi problem and the overall install issues.

Friday, June 09, 2006

Google Browser Sync

Well, this looks interesting.

Finding a field in Java

A large motivation for me starting this blog was to help me remember useful snippets of code. This is a good example:

Field getField(Class clazz, final String fieldName)
        throws NoSuchFieldException {
    for (; null != clazz; clazz = clazz.getSuperclass()) {
        try {
            final Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        } catch (final NoSuchFieldException e) { }
    }

    throw new NoSuchFieldException(fieldName);
}

Very simple code, no? But a fact I seem to sometime forget is that inherited fields do not show up in reflection; one must reflect down into the appropriate superclass where the field was declared. The simple method handles the details easily enough.

Thursday, June 08, 2006

Against agilism

As much as I am a fan of agile development methodologies, I must admit that Cedric Beust has some good points against agile.

I need more time to think his post through. It is meaty, lengthy and well-written. It is also a bit abrasive, but I enjoy that style.

Wednesday, June 07, 2006

IntelliJ IDEA build 5321

After several builds that gave me a lot of trouble, IntelliJ IDEA build 5321 seems to hit the spot.

With several of the previous EAP builds the IDE leaked memory fairly badly and I often hit mysterious lock ups for minutes at a time. The only solution was restarting. Worse was that many times the dialog to mail exception reports to JetBrains itself would fail.

Build 5321 does not seem to have these flaws, happily, and seems robust enough for daily use.

Monday, May 22, 2006

Learning more about web continuations

Web continuations fascinate me, but alas I have no current project in which to work on them. So I try to keep up in my reading instead.

I ran across this lengthy post on the "abandoned session" problem by Blushish Coder, one of my favorite code bloggers. Meanwhile, I bide my time looking for the right project for proposing continuations.

Friday, May 19, 2006

Leo Simons on unit tests for complex software

Leo Simons has a useful post on how to organize unit tests for complex software. Included is a nifty chart which explains his post well. I feel chart envy setting in.

Tuesday, May 02, 2006

A debugging glass pane

I've started on a largish Java project (4000+ classes) with a complex Swing UI front-end and have had difficulty working out just which class is in play for a given visual component.

Glass pane to the rescue.

To help me out, I've written a small debugging glass pane based on a universal right-click handler found on Google similar to the example provided in the Swing documentation.

Use is simple:

JFrame frame = ...;
DebugGlassPane.setDebugGlassPaneOn(frame);

Now when you hover the mouse over a component in the content pane, up pops a tooltip identifying the class and instance name. If you want to change the displayed information, edit createTipText(Component).

The complete class:

public class DebugGlassPane
        extends JComponent
        implements MouseListener, MouseMotionListener {
    private final JLayeredPane layered;

    public static void setDebugGlassPaneOn(final JFrame frame) {
        final Component glass = new DebugGlassPane(frame);
        // Must be in this order
        frame.setGlassPane(glass);
        glass.setVisible(true);
    }

    public DebugGlassPane(final JFrame frame) {
        layered = frame.getLayeredPane();

        addMouseListener(this);
        addMouseMotionListener(this);
    }

    public void mouseMoved(final MouseEvent e) {
        setToolTipText(createTipText(getChildUnderMouse(e)));

        redispatchMouseEvent(e);
    }

    public void mouseDragged(final MouseEvent e) {
        redispatchMouseEvent(e);
    }

    public void mouseClicked(final MouseEvent e) {
        redispatchMouseEvent(e);
    }

    public void mouseEntered(final MouseEvent e) {
        redispatchMouseEvent(e);
    }

    public void mouseExited(final MouseEvent e) {
        redispatchMouseEvent(e);
    }

    public void mousePressed(final MouseEvent e) {
        redispatchMouseEvent(e);
    }

    public void mouseReleased(final MouseEvent e) {
        redispatchMouseEvent(e);
    }

    private String createTipText(final Component c) {
        return "<html>Class: <b>" + c.getClass().getName()
                + "</b><br>Name: <i>" + c.getName();
    }

    private void redispatchMouseEvent(final MouseEvent e) {
        final Component component = getChildUnderMouse(e);

        // E.g., popup menus
        if (component == null) return;

        // redispatch the event
        component.dispatchEvent(
                SwingUtilities.convertMouseEvent(this, e, component));
    }

    private Component getChildUnderMouse(final MouseEvent e) {
        // get the mouse click point relative to the content pane
        final Point containerPoint = SwingUtilities.convertPoint(this,
                e.getPoint(), layered);

        return SwingUtilities.getDeepestComponentAt(layered,
                containerPoint.x, containerPoint.y);
    }
}

Surprisingly, I had a difficult time finding such a class already coded up somewhere. It is quite handy for exploring the UI.

UPDATE: An important fix (already incorporated in the code, above). Say you have a menu bar. The current code throws a NullPointerException. The problem is that all the work is relative to the content pane, but a JFrame can have visual space which recieves mouse events but is not part of the content.

The fix is simple: change getChildUnderMouse(MouseEvent) to work with the component from frame.getLayeredPane() instead of frame.getContentPane(). Sun has a good explanatory diagram and description making this point clear.

Sunday, April 23, 2006

Nifty JDBC 4.0

Today I've been playing with some of the new JDBC 4.0 features in the latest JDK6 beta. In particular, I've been scouting around for a lighter-weight alternative to Hibernate when working with small-sized projects, and one that uses annotations rather than XML.

A few weeks ago I tried out Ammentos, and although in the right direction it did not quite handle everything I threw at it. JDBC 4.0, however, seems just the thing with a little extra effort.

First some setup. After grabbing the latest JDK6 build I needed a database with a JDBC 4.0 driver. Some googling revealed that the also nifty Apache Derby database project is such a database, however only special daily builds have support turned on. Fair enough, it was only a download away and JDK6 is still in beta.

Finally, time for some code (ignoring imports, error handling and such):

public static void main(final String[] args)
        throws ClassNotFoundException, SQLException {
    Class.forName(EmbeddedDriver.class.getName());

    final Connection connection = DriverManager.getConnection(
        "jdbc:derby:test-database;create=true");

    final Statement statement = connection.createStatement();
    statement.execute("CREATE TABLE BOB"
            + "(ID INT NOT NULL GENERATED ALWAYS AS IDENTITY,"
            + " NAME VARCHAR(32))");

    connection.setAutoCommit(false);
    connection.setTransactionIsolation(TRANSACTION_SERIALIZABLE);

    final BobQuery query = connection.createQueryObject(BobQuery.class);

    final String name = "the Builder";
        
    final BobKeys key = query.addBob(name).get(0);
    final Bob bob = query.findBobByName(name).get(0);

    System.out.println("inserted = selected? " + key.id.equals(bob.id));

    query.close();
    statement.execute("DROP TABLE BOB");
    connection.commit();
    statement.close();
    connection.close();
}

public class Bob {
    public BigDecimal id;
    public String name;
}

@AutoGeneratedKeys
public class BobKeys {
    public BigDecimal id;
}

public interface BobQuery
        extends BaseQuery {
    @Update(sql = "INSERT INTO BOB(name) VALUES(?1)",
            keys = RETURNED_KEYS_DRIVER_DEFINED)
    DataSet addBob(final String name);

    @Select("SELECT * FROM BOB WHERE name = ?1")
    DataSet findBobByName(final String name);

    @Select("SELECT * FROM BOB")
    DataSet findAllBobs();

    @Update("DELETE FROM BOB")
    int removeAllBobs();
}

Everything worked!

(And Derby helpfully creates a scratch database in-place given the proper JDBC URL.)

There were some gotchas:

  • The Derby driver did not seem to use the new service feature at least for the embedded case shown here.
  • The type for primary key identifiers is BigDecimal, not Long as I guessed at. Improved javadocs would help here.
  • I had little luck with the {fieldName} syntax shown in the @Update annotation javadocs for auto-generated keys. I am unsure if the driver, the JDK, the javadocs or the coder is at fault.

JDBC 4.0 is particularly well thought-out for custom persistence layers. I suspect much was borrowed conceptually from Hibernate's excellent work. To use the common DAO pattern as an example, just consider insertion:

public int addBob(final Bob bob) {
    final DataSet keys = addBob(bob.getName());

    if (keys.isEmpty()) return 0;

    bob.id = keys.get(0).id;

    return keys.size();
}

So much boilerplate code saved, a real boon to the coder in the trenches; the code is very readable; no SQLException peppering the entire DAO layer. (Of course my curiosity rises: what does happen when the database cannot insert? — I need to read more on getSQLWarnings().)

JDBC 4.0 is nifty indeed.

UPDATE: As noted in one of the trackbacks, the Derby JDBC 4.0 driver now supports the service discovery mechanism. No more need to call Class.forName. Excellent.