GeneratedKeysReader.java
package org.codefilarete.stalactite.sql.statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import org.codefilarete.stalactite.sql.result.ResultSetIterator;
import org.codefilarete.stalactite.sql.statement.binder.ResultSetReader;
import org.codefilarete.tool.collection.Iterables;
import org.codefilarete.tool.function.SerializableThrowingBiFunction;
/**
* Default implementation of a {@link ResultSet} returned by {@link Statement#getGeneratedKeys()}.
* It only reads one generated value that must be in the first column of the returned {@link ResultSet}.
* It assumes that the key is a primitive {@link Long}.
* As specified by {@link Statement#getGeneratedKeys()}, it expected that a key is returned for each inserted row.
*
* The key is only used to fill the rows returned by {@link #convert(WriteOperation)}, it's not used for ResultSet read.
*
* As databases support differently generated keys feature, you may override some behaviors. For instance if the
* key is not in the first column or you don't want to read it as a long, you may override {@link #readKey(ResultSet)}.
*
* If only one row is returned even in multi-row statement (Derby behaves like this, see https://issues.apache.org/jira/browse/DERBY-3609),
* then you may override {@link #convert(WriteOperation)} to simulate multiple rows reading.
*
* @param <I> generated keys type
* @author Guillaume Mary
*/
public class GeneratedKeysReader<I> {
private final String keyName;
/** Underlying reader of generated value */
private final ResultSetReader<I> typeReader;
/**
* Constructor.
*
* @param keyName column name to be read on generated {@link ResultSet}
* @param resultSetGetter reader of generated key column
*/
public GeneratedKeysReader(String keyName, SerializableThrowingBiFunction<ResultSet, String, I, SQLException> resultSetGetter) {
this(keyName, ResultSetReader.ofMethodReference(resultSetGetter));
}
/**
* Constructor.
*
* @param keyName column name to be read on generated {@link ResultSet}
* @param typeReader reader of generated key column
*/
public GeneratedKeysReader(String keyName, ResultSetReader<I> typeReader) {
this.keyName = keyName;
this.typeReader = typeReader;
}
public String getKeyName() {
return keyName;
}
/**
* Method that must be called after insert or update operation to read generated values through {@link PreparedStatement#getGeneratedKeys()}.
* Please note that {@link PreparedStatement} should have been created with {@link java.sql.Connection#prepareStatement(String, int)} and
* {@link Statement#RETURN_GENERATED_KEYS} argument.
*
*
* @param writeOperation any insert operation (can't be a {@link PreparedStatement} because some implementations needs more information (see Derby override)
* @return a {@link List} of database-generated keys during operation execution
* @throws SQLException in case of reading problem
*/
public List<I> convert(WriteOperation writeOperation) throws SQLException {
try (ResultSet generatedKeys = writeOperation.preparedStatement.getGeneratedKeys()) {
return convert(generatedKeys);
}
}
public List<I> convert(ResultSet generatedKeys) {
ResultSetIterator<I> iterator = new ResultSetIterator<I>(generatedKeys) {
@Override
public I convert(ResultSet rs) throws SQLException {
return readKey(rs);
}
};
return Iterables.copy(iterator);
}
protected I readKey(ResultSet rs) throws SQLException {
return typeReader.get(rs, getKeyName());
}
}