Make use of getGeneratedKeys in lastInsertedId.

This allows us to do the necessary hackery for MySQL which reports the
generated key as GENERATED_KEY instead of via its actual column name.
This commit is contained in:
Michael Bayne
2014-05-02 11:15:11 -07:00
parent 2e0f9b29e6
commit a473ff2ef1
9 changed files with 46 additions and 17 deletions
@@ -36,9 +36,21 @@ public abstract class BaseLiaison implements DatabaseLiaison
// from DatabaseLiaison // from DatabaseLiaison
public abstract boolean isTransientException (SQLException sqe); public abstract boolean isTransientException (SQLException sqe);
@Deprecated
public int lastInsertedId (Connection conn, String table, String column) throws SQLException {
return lastInsertedId(conn, null, table, column);
}
// from DatabaseLiaison // from DatabaseLiaison
public abstract int lastInsertedId (Connection conn, String table, String column) public int lastInsertedId (Connection conn, Statement istmt, String table, String column)
throws SQLException; throws SQLException
{
// if this JDBC driver supports getGeneratedKeys, use it!
if (istmt != null && conn.getMetaData().supportsGetGeneratedKeys()) {
return istmt.getGeneratedKeys().getInt(column);
}
return -1;
}
// from DatabaseLiaison // from DatabaseLiaison
public boolean tableExists (Connection conn, String name) throws SQLException public boolean tableExists (Connection conn, String name) throws SQLException
@@ -5,6 +5,7 @@
package com.samskivert.jdbc; package com.samskivert.jdbc;
import java.sql.Statement;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
@@ -43,12 +44,19 @@ public interface DatabaseLiaison
*/ */
public boolean isTransientException (SQLException sqe); public boolean isTransientException (SQLException sqe);
/** @deprecated Use version that takes the insert statement. */
@Deprecated
public int lastInsertedId (Connection conn, String table, String column) throws SQLException;
/** /**
* Attempts as dialect-agnostic an interface as possible to the ability of certain databases to * Attempts as dialect-agnostic an interface as possible to the ability of certain databases to
* auto-generated numerical values for i.e. key columns; there is MySQL's AUTO_INCREMENT and * auto-generated numerical values for i.e. key columns; there is MySQL's AUTO_INCREMENT and
* PostgreSQL's DEFAULT nextval(sequence), for example. * PostgreSQL's DEFAULT nextval(sequence), for example.
*
* @param istmt the insert statement that generated the keys. May be null if the ORM doesn't
* have the statement handy.
*/ */
public int lastInsertedId (Connection conn, String table, String column) public int lastInsertedId (Connection conn, Statement istmt, String table, String column)
throws SQLException; throws SQLException;
/** /**
@@ -46,13 +46,6 @@ public class DefaultLiaison extends BaseLiaison
// nothing doing // nothing doing
} }
@Override // from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column)
throws SQLException
{
return -1;
}
@Override // from DatabaseLiaison @Override // from DatabaseLiaison
public String columnSQL (String column) public String columnSQL (String column)
{ {
@@ -73,8 +73,12 @@ public class HsqldbLiaison extends BaseLiaison
} }
@Override // from DatabaseLiaison @Override // from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) throws SQLException public int lastInsertedId (Connection conn, Statement istmt, String table, String column)
throws SQLException
{ {
int id = super.lastInsertedId(conn, istmt, table, column);
if (id >= 0) return id;
// HSQL does not keep track of per-table-and-column insertion data, so we are pretty much // HSQL does not keep track of per-table-and-column insertion data, so we are pretty much
// going on blind faith here that we're fetching the right ID. In the overwhelming number // going on blind faith here that we're fetching the right ID. In the overwhelming number
// of cases that will be so, but it's still not pretty. // of cases that will be so, but it's still not pretty.
@@ -50,7 +50,7 @@ public abstract class JORARepository extends SimpleRepository
throws SQLException, PersistenceException throws SQLException, PersistenceException
{ {
table.insert(conn, object); table.insert(conn, object);
return liaison.lastInsertedId(conn, table.getName(), "TODO"); return liaison.lastInsertedId(conn, null, table.getName(), "TODO");
} }
}); });
} }
@@ -250,7 +250,7 @@ public abstract class JORARepository extends SimpleRepository
{ {
if (table.update(conn, object) == 0) { if (table.update(conn, object) == 0) {
table.insert(conn, object); table.insert(conn, object);
return liaison.lastInsertedId(conn, table.getName(), "TODO"); return liaison.lastInsertedId(conn, null, table.getName(), "TODO");
} }
return -1; return -1;
} }
@@ -50,8 +50,15 @@ public class MySQLLiaison extends BaseLiaison
} }
@Override // from DatabaseLiaison @Override // from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) throws SQLException public int lastInsertedId (Connection conn, Statement istmt, String table, String column)
throws SQLException
{ {
// MySQL hackily reports the last inserted key as GENERATED_KEY, so we call super with that
// "column name"; but if that doesn't work (we're using an old JDBC driver without support
// for returning generated keys), fall back to the old old method
int id = super.lastInsertedId(conn, istmt, table, "GENERATED_KEY");
if (id >= 0) return id;
// MySQL does not keep track of per-table-and-column insertion data, so we are pretty much // MySQL does not keep track of per-table-and-column insertion data, so we are pretty much
// going on blind faith here that we're fetching the right ID. In the overwhelming number // going on blind faith here that we're fetching the right ID. In the overwhelming number
// of cases that will be so, but it's still not pretty. // of cases that will be so, but it's still not pretty.
@@ -35,8 +35,13 @@ public class PostgreSQLLiaison extends BaseLiaison
} }
// from DatabaseLiaison // from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) throws SQLException public int lastInsertedId (Connection conn, Statement istmt, String table, String column)
throws SQLException
{ {
// try the default first, which uses JDBC's getGeneratedKeys mechanism
int id = super.lastInsertedId(conn, istmt, table, column);
if (id >= 0) return id;
// PostgreSQL's support for auto-generated ID's comes in the form of appropriately named // PostgreSQL's support for auto-generated ID's comes in the form of appropriately named
// sequences and DEFAULT nextval(sequence) modifiers in the ID columns. To get the next ID, // sequences and DEFAULT nextval(sequence) modifiers in the ID columns. To get the next ID,
// we use the currval() method which is set in a database sessions when any given sequence // we use the currval() method which is set in a database sessions when any given sequence
@@ -246,7 +246,7 @@ public class JDBCTableSiteIdentifier implements SiteIdentifier
if (1 != stmt.executeUpdate()) { if (1 != stmt.executeUpdate()) {
throw new PersistenceException("Not inserted " + site); throw new PersistenceException("Not inserted " + site);
} }
site.siteId = liaison.lastInsertedId(conn, "sites", "siteId"); site.siteId = liaison.lastInsertedId(conn, stmt, "sites", "siteId");
} finally { } finally {
JDBCUtil.close(stmt); JDBCUtil.close(stmt);
@@ -398,7 +398,7 @@ public class UserRepository extends JORARepository
try { try {
_utable.insert(conn, user); _utable.insert(conn, user);
// update the userid now that it's known // update the userid now that it's known
user.userId = liaison.lastInsertedId(conn, _utable.getName(), "userId"); user.userId = liaison.lastInsertedId(conn, null, _utable.getName(), "userId");
// nothing to return // nothing to return
return null; return null;