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, notLongas I guessed at. Improved javadocs would help here. - I had little luck with the
{fieldName}syntax shown in the@Updateannotation 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.
Hi
ReplyDeleteThe new service feature is available in derby now got checked on 24th the day after you wrote the blog :).
You will need build 81 or latter of jdk1.6 (mustang) to use this feature.
The javadocs had a typo for the update annotation and {field} that functionality does not exist.
ReplyDeleteThe EoD layer will throw a SQLRuntimeException which you can obtain the SQLException from if you desire
The new service feature worked fine -- thanks!
ReplyDelete