Only use RETURN_GENERATED_KEYS on inserts.
This works around "unsophisticated" Postgres JDBC drivers which blindly tack "RETURNING *" onto a query if RETURN_GENERATED_KEYS is supplied.
This commit is contained in:
@@ -321,7 +321,7 @@ public abstract class DepotRepository
|
|||||||
}
|
}
|
||||||
builder.newQuery(new InsertClause(pClass, _result, identityFields));
|
builder.newQuery(new InsertClause(pClass, _result, identityFields));
|
||||||
|
|
||||||
PreparedStatement stmt = builder.prepare(conn);
|
PreparedStatement stmt = builder.prepareInsert(conn);
|
||||||
int mods = stmt.executeUpdate();
|
int mods = stmt.executeUpdate();
|
||||||
// run any post-factum value generators and potentially generate our key
|
// run any post-factum value generators and potentially generate our key
|
||||||
if (_key == null) {
|
if (_key == null) {
|
||||||
@@ -579,7 +579,7 @@ public abstract class DepotRepository
|
|||||||
}
|
}
|
||||||
builder.newQuery(new InsertClause(pClass, _result, identityFields));
|
builder.newQuery(new InsertClause(pClass, _result, identityFields));
|
||||||
|
|
||||||
PreparedStatement stmt = builder.prepare(conn);
|
PreparedStatement stmt = builder.prepareInsert(conn);
|
||||||
int mods = stmt.executeUpdate();
|
int mods = stmt.executeUpdate();
|
||||||
|
|
||||||
// run any post-factum value generators and potentially generate our key
|
// run any post-factum value generators and potentially generate our key
|
||||||
|
|||||||
@@ -62,30 +62,19 @@ public abstract class SQLBuilder
|
|||||||
public PreparedStatement prepare (Connection conn)
|
public PreparedStatement prepare (Connection conn)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
checkState(_buildVisitor != null, "Cannot prepare query until it's been built.");
|
return prepare(conn, conn.prepareStatement(buildQuery()));
|
||||||
|
|
||||||
PreparedStatement stmt = conn.prepareStatement(
|
|
||||||
_buildVisitor.getQuery(), PreparedStatement.RETURN_GENERATED_KEYS);
|
|
||||||
|
|
||||||
int argIx = 1;
|
|
||||||
for (BuildVisitor.Bindable bindable : _buildVisitor.getBindables()) {
|
|
||||||
try {
|
|
||||||
bindable.doBind(conn, stmt, argIx);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warning("Failed to bind statement argument", "argIx", argIx, e);
|
|
||||||
}
|
|
||||||
argIx ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PersistenceContext.DEBUG) {
|
|
||||||
log.info("SQL: " + stmt.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return stmt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String nullify (String str) {
|
/**
|
||||||
return (str != null && str.length() > 0) ? str : null;
|
* A variant of {@link #prepare} that must be used for INSERTs. Due to a Postgres JDBC driver
|
||||||
|
* bug, we need to restrict the use of RETURN_GENERATED_KEYS to insert queries only, which
|
||||||
|
* means we need to know when we're doing an insert.
|
||||||
|
*/
|
||||||
|
public PreparedStatement prepareInsert (Connection conn)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
return prepare(
|
||||||
|
conn, conn.prepareStatement(buildQuery(), PreparedStatement.RETURN_GENERATED_KEYS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,6 +118,69 @@ public abstract class SQLBuilder
|
|||||||
return coldef;
|
return coldef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add full-text search capabilities, as defined by the provided {@link FullTextIndex}, on
|
||||||
|
* the table associated with the given {@link DepotMarshaller}. This is a highly database
|
||||||
|
* specific operation and must thus be implemented by each dialect subclass.
|
||||||
|
*
|
||||||
|
* @see FullTextIndex
|
||||||
|
*/
|
||||||
|
public abstract <T extends PersistentRecord> boolean addFullTextSearch (
|
||||||
|
Connection conn, DepotMarshaller<T> marshaller, FullTextIndex fts)
|
||||||
|
throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the supplied column is an internal consideration of this {@link SQLBuilder},
|
||||||
|
* e.g. PostgreSQL's full text search data is stored in a table column that should otherwise
|
||||||
|
* not be visible to Depot; this method helps mask it.
|
||||||
|
*/
|
||||||
|
public abstract boolean isPrivateColumn (
|
||||||
|
String column, Map<String, FullTextIndex> fullTextIndexes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the supplied index is an internal consideration of this {@link SQLBuilder},
|
||||||
|
* e.g. PostgreSQL automatically creates indexes that end in _key for unique columns, and its
|
||||||
|
* primary keys have indices ending in _pkey.
|
||||||
|
*/
|
||||||
|
public abstract boolean isPrivateIndex (
|
||||||
|
String index, Map<String, FullTextIndex> fullTextIndexes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Figure out what full text search indexes already exist on this table and add the names of
|
||||||
|
* those indexes to the supplied target set.
|
||||||
|
*/
|
||||||
|
public abstract void getFtsIndexes (
|
||||||
|
Iterable<String> columns, Iterable<String> indexes, Set<String> target);
|
||||||
|
|
||||||
|
protected String buildQuery () {
|
||||||
|
checkState(_buildVisitor != null, "Cannot prepare query until it's been built.");
|
||||||
|
return _buildVisitor.getQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PreparedStatement prepare (Connection conn, PreparedStatement stmt)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
int argIx = 1;
|
||||||
|
for (BuildVisitor.Bindable bindable : _buildVisitor.getBindables()) {
|
||||||
|
try {
|
||||||
|
bindable.doBind(conn, stmt, argIx);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warning("Failed to bind statement argument", "argIx", argIx, e);
|
||||||
|
}
|
||||||
|
argIx ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PersistenceContext.DEBUG) {
|
||||||
|
log.info("SQL: " + stmt.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String nullify (String str) {
|
||||||
|
return (str != null && str.length() > 0) ? str : null;
|
||||||
|
}
|
||||||
|
|
||||||
protected void maybeMutateForPrimitive (Field field, ColumnDefinition coldef)
|
protected void maybeMutateForPrimitive (Field field, ColumnDefinition coldef)
|
||||||
{
|
{
|
||||||
// Java primitive types cannot be null, so we provide a default value for these columns
|
// Java primitive types cannot be null, so we provide a default value for these columns
|
||||||
@@ -183,40 +235,6 @@ public abstract class SQLBuilder
|
|||||||
return "false";
|
return "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add full-text search capabilities, as defined by the provided {@link FullTextIndex}, on
|
|
||||||
* the table associated with the given {@link DepotMarshaller}. This is a highly database
|
|
||||||
* specific operation and must thus be implemented by each dialect subclass.
|
|
||||||
*
|
|
||||||
* @see FullTextIndex
|
|
||||||
*/
|
|
||||||
public abstract <T extends PersistentRecord> boolean addFullTextSearch (
|
|
||||||
Connection conn, DepotMarshaller<T> marshaller, FullTextIndex fts)
|
|
||||||
throws SQLException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the supplied column is an internal consideration of this {@link SQLBuilder},
|
|
||||||
* e.g. PostgreSQL's full text search data is stored in a table column that should otherwise
|
|
||||||
* not be visible to Depot; this method helps mask it.
|
|
||||||
*/
|
|
||||||
public abstract boolean isPrivateColumn (
|
|
||||||
String column, Map<String, FullTextIndex> fullTextIndexes);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the supplied index is an internal consideration of this {@link SQLBuilder},
|
|
||||||
* e.g. PostgreSQL automatically creates indexes that end in _key for unique columns, and its
|
|
||||||
* primary keys have indices ending in _pkey.
|
|
||||||
*/
|
|
||||||
public abstract boolean isPrivateIndex (
|
|
||||||
String index, Map<String, FullTextIndex> fullTextIndexes);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Figure out what full text search indexes already exist on this table and add the names of
|
|
||||||
* those indexes to the supplied target set.
|
|
||||||
*/
|
|
||||||
public abstract void getFtsIndexes (
|
|
||||||
Iterable<String> columns, Iterable<String> indexes, Set<String> target);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridden by subclasses to create a dialect-specific {@link BuildVisitor}.
|
* Overridden by subclasses to create a dialect-specific {@link BuildVisitor}.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user