DerbyParameterBinders.java
package org.codefilarete.stalactite.sql.derby.statement.binder;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import org.codefilarete.stalactite.sql.statement.binder.DefaultParameterBinders;
import org.codefilarete.stalactite.sql.statement.binder.InMemoryBlobSupport;
import org.codefilarete.stalactite.sql.statement.binder.LambdaParameterBinder;
import org.codefilarete.stalactite.sql.statement.binder.LocalTimeBinder;
import org.codefilarete.stalactite.sql.statement.binder.NullAwareParameterBinder;
import org.codefilarete.stalactite.sql.statement.binder.NullAwarePreparedStatementWriter;
import org.codefilarete.stalactite.sql.statement.binder.NullAwareResultSetReader;
import org.codefilarete.stalactite.sql.statement.binder.ParameterBinder;
import org.codefilarete.stalactite.sql.statement.binder.ResultSetReader.LambdaResultSetReader;
import org.codefilarete.tool.Nullable;
import org.codefilarete.tool.io.IOs;
import static org.codefilarete.stalactite.sql.statement.binder.DefaultPreparedStatementWriters.BINARYSTREAM_WRITER;
import static org.codefilarete.stalactite.sql.statement.binder.DefaultPreparedStatementWriters.BLOB_WRITER;
import static org.codefilarete.stalactite.sql.statement.binder.DefaultPreparedStatementWriters.BYTES_WRITER;
/**
* @author Guillaume Mary
*/
public final class DerbyParameterBinders {
/**
* Equivalent to {@link DefaultParameterBinders#BINARYSTREAM_BINDER} but makes a copy of the read binary stream from ResultSet, else later reading fails
* with "java.io.IOException: The object is already closed",
* even it the connection is still open and the ResultSet hasn't changed of row. Looks weird.
* Looks like https://issues.apache.org/jira/browse/DERBY-6341 but I don't get where I'm wrong (don't see where I read twice the ResultSet).
*/
public static final ParameterBinder<InputStream> BINARYSTREAM_BINDER = new LambdaParameterBinder<>(new LambdaResultSetReader<>((ResultSet resultSet, String columnName) ->
Nullable.nullable(resultSet.getBinaryStream(columnName)).mapThrower(inputStream -> {
try {
return IOs.toByteArrayInputStream(inputStream);
} catch (IOException e) {
throw new SQLException(e);
}
}).get(), InputStream.class), BINARYSTREAM_WRITER);
/**
* Equivalent to {@link DefaultParameterBinders#BYTES_BINDER} but makes a copy of the read binary stream from ResultSet, else later reading fails
* with "Stream or LOB value cannot be retrieved more than once".
*/
public static final ParameterBinder<byte[]> BYTES_BINDER = new LambdaParameterBinder<>(new LambdaResultSetReader<>((ResultSet resultSet, String columnName) ->
Nullable.nullable(resultSet.getBytes(columnName)).mapThrower(bytes -> Arrays.copyOf(bytes, bytes.length)).get(), byte[].class), BYTES_WRITER);
/**
* Equivalent to {@link DefaultParameterBinders#BLOB_BINDER} but makes a copy of the read binary stream from ResultSet, else later reading fails
* with "Stream or LOB value cannot be retrieved more than once".
*/
public static final ParameterBinder<Blob> BLOB_BINDER = new LambdaParameterBinder<>(new LambdaResultSetReader<>((ResultSet resultSet, String columnName) ->
Nullable.nullable(resultSet.getBlob(columnName)).mapThrower(blob -> new InMemoryBlobSupport(blob.getBytes(1, (int) blob.length()))).get(), Blob.class), BLOB_WRITER);
/**
* Dedicated {@link LocalDateTime} {@link ParameterBinder} for Derby because its TIMESTAMP SQL type stores all 9 nanosecond digits (at least
* in 10.5, wasn't the case in some previous versions), therefore it defers from other databases and SQL-92 standard that stores 6 digits by
* default. Furthermore, Derby TIMESTAMP can't be configured for precision. So, to keep a homogeneous behavior between databases it was choosen
* to store only 6 firsts digits of given {@link LocalDateTime} nanos : this binder does it.
*/
public static final ParameterBinder<LocalDateTime> LOCALDATETIME_BINDER = new NullAwareParameterBinder<>(
new NullAwareResultSetReader<>(DefaultParameterBinders.LOCALDATETIME_BINDER),
new NullAwarePreparedStatementWriter<>((preparedStatement, valueIndex, localDateTime) -> {
localDateTime = localDateTime.minusNanos(localDateTime.getNano() % 1000); // 3 last digits are erased (input is not changed)
Timestamp timestamp = Timestamp.valueOf(localDateTime);
preparedStatement.setTimestamp(valueIndex, timestamp);
}));
/**
* Dedicated {@link LocalTime} {@link ParameterBinder} for Derby because its TIMESTAMP SQL type stores all 9 nanosecond digits (at least
* in 10.5, wasn't the case in some previous versions), therefore it defers from other databases and SQL-92 standard that stores 6 digits by
* default. Furthermore, Derby TIMESTAMP can't be configured for precision. So, to keep a homogeneous behavior between databases it was choosen
* to store only 6 firsts digits of given {@link LocalDateTime} nanos : this binder does it.
*/
public static final ParameterBinder<LocalTime> LOCALTIME_BINDER = new NullAwareParameterBinder<>(
new NullAwareResultSetReader<>(DefaultParameterBinders.LOCALTIME_BINDER),
new NullAwarePreparedStatementWriter<>((preparedStatement, valueIndex, localDateTime) -> {
localDateTime = localDateTime.minusNanos(localDateTime.getNano() % 1000); // 3 last digits are erased (input is not changed)
Timestamp timestamp = Timestamp.valueOf(localDateTime.atDate(LocalTimeBinder.DEFAULT_TIMESTAMP_REFERENCE_DATE));
preparedStatement.setTimestamp(valueIndex, timestamp);
}));
private DerbyParameterBinders() {
}
}