buckelieg.jdbc.JDBCDefaults Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdbc-fn Show documentation
Show all versions of jdbc-fn Show documentation
Functional style programming with plain JDBC
The newest version!
/*
* Copyright 2024- Anatoly Kutyakov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package buckelieg.jdbc;
import buckelieg.jdbc.fn.TryBiFunction;
import buckelieg.jdbc.fn.TryFunction;
import buckelieg.jdbc.fn.TryTriConsumer;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.*;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import static buckelieg.jdbc.Utils.entry;
import static java.lang.Math.max;
import static java.sql.JDBCType.*;
import static java.util.stream.Collectors.joining;
enum JDBCDefaults {
;
private static final int DEFAULT_BUFFER_SIZE = 4096; // TODO make customizable
private static final Map> defaultReaders = new HashMap<>();
private static final Map> defaultWriters = new HashMap<>();
static TryFunction, SQLException> defaultMapper() {
return new DefaultValueMapper();
}
static Map defaultMapper(ValueReader reader) throws SQLException {
Metadata meta = reader.meta();
Map result = new HashMap<>(meta.count());
reader.meta().forEachColumn(index -> result.put(meta.getName(index), reader(meta.getSQLType(index)).apply(reader, index)));
return result;
}
static final class DefaultValueMapper implements TryFunction, SQLException> {
private List, TryBiFunction>> colReaders;
private final AtomicReference, SQLException>> mapper = new AtomicReference<>();
@Override
public Map apply(ValueReader valueReader) throws SQLException {
return mapper.updateAndGet(instance -> {
if (null == instance) {
Metadata meta = valueReader.meta();
int columnCount = meta.count();
colReaders = new ArrayList<>(columnCount);
for (int col = 1; col <= columnCount; col++) {
colReaders.add(entry(entry(meta.getLabel(col), col), reader(meta.getSQLType(col))));
}
instance = getter -> {
Map result = new LinkedHashMap<>(columnCount);
for (Map.Entry, TryBiFunction> e : colReaders) {
result.put(e.getKey().getKey(), e.getValue().apply(getter, e.getKey().getValue()));
}
return result;
};
}
return instance;
}).apply(valueReader);
}
}
static {
defaultReaders.put(ARRAY, ValueReader::getArray);
defaultReaders.put(BINARY, ValueReader::getBytes);
defaultReaders.put(BIGINT, ValueReader::getLong);
defaultReaders.put(BLOB, longVarBinaryReader(DEFAULT_BUFFER_SIZE, (rs, index) -> {
Blob blob = rs.getBlob(index);
return null == blob ? null : blob.getBinaryStream();
}));
defaultReaders.put(BIT, ValueReader::getBoolean);
defaultReaders.put(CHAR, ValueReader::getString);
defaultReaders.put(CLOB, longVarCharReader(clobReader(ValueReader::getClob)));
defaultReaders.put(DATE, ValueReader::getDate);
defaultReaders.put(DECIMAL, ValueReader::getBigDecimal);
defaultReaders.put(DOUBLE, ValueReader::getDouble);
defaultReaders.put(FLOAT, ValueReader::getDouble);
defaultReaders.put(INTEGER, ValueReader::getInt);
defaultReaders.put(JAVA_OBJECT, ValueReader::getObject);
defaultReaders.put(LONGVARBINARY, longVarBinaryReader(DEFAULT_BUFFER_SIZE, ValueReader::getBinaryStream));
defaultReaders.put(LONGVARCHAR, longVarCharReader(ValueReader::getCharacterStream));
defaultReaders.put(LONGNVARCHAR, longVarCharReader(ValueReader::getNCharacterStream));
defaultReaders.put(NCLOB, longVarCharReader(clobReader(ValueReader::getNClob)));
defaultReaders.put(NUMERIC, ValueReader::getBigDecimal);
defaultReaders.put(OTHER, ValueReader::getObject);
defaultReaders.put(REAL, ValueReader::getFloat);
defaultReaders.put(SMALLINT, ValueReader::getShort);
defaultReaders.put(TIME, ValueReader::getTime);
defaultReaders.put(TIME_WITH_TIMEZONE, ValueReader::getTime);
defaultReaders.put(TIMESTAMP, ValueReader::getTimestamp);
defaultReaders.put(TIMESTAMP_WITH_TIMEZONE, ValueReader::getTimestamp);
defaultReaders.put(TINYINT, ValueReader::getByte);
defaultReaders.put(VARBINARY, ValueReader::getBytes);
defaultReaders.put(VARCHAR, ValueReader::getString);
defaultWriters.put(ARRAY, TryTriConsumer.of(ValueWriter::setArray));
defaultWriters.put(BIGINT, TryTriConsumer.of(ValueWriter::setLong));
defaultWriters.put(BINARY, TryTriConsumer.of(ValueWriter::setBytes));
defaultWriters.put(BIT, TryTriConsumer.of(ValueWriter::setBoolean));
defaultWriters.put(BLOB, TryTriConsumer.of(ValueWriter::setBlob));
defaultWriters.put(CHAR, TryTriConsumer.of(ValueWriter::setString));
defaultWriters.put(CLOB, TryTriConsumer.of(ValueWriter::setClob));
defaultWriters.put(DATE, TryTriConsumer.of(ValueWriter::setDate));
defaultWriters.put(DECIMAL, TryTriConsumer.of(ValueWriter::setBigDecimal));
defaultWriters.put(DOUBLE, TryTriConsumer.of(ValueWriter::setDouble));
defaultWriters.put(FLOAT, TryTriConsumer.of(ValueWriter::setDouble));
defaultWriters.put(INTEGER, TryTriConsumer.of(ValueWriter::setInt));
defaultWriters.put(JAVA_OBJECT, JDBCDefaults::setObject);
defaultWriters.put(LONGVARBINARY, TryTriConsumer.of(ValueWriter::setBinaryStream));
defaultWriters.put(LONGVARCHAR, TryTriConsumer.of(ValueWriter::setCharacterStream));
defaultWriters.put(LONGNVARCHAR, TryTriConsumer.of(ValueWriter::setNCharacterStream));
defaultWriters.put(NCLOB, TryTriConsumer.of(ValueWriter::setNClob));
defaultWriters.put(NUMERIC, TryTriConsumer.of(ValueWriter::setBigDecimal));
defaultWriters.put(OTHER, JDBCDefaults::setObject);
defaultWriters.put(REAL, TryTriConsumer.of(ValueWriter::setFloat));
defaultWriters.put(SMALLINT, TryTriConsumer.of(ValueWriter::setShort));
defaultWriters.put(TIME, TryTriConsumer.of(ValueWriter::setTime));
defaultWriters.put(TIME_WITH_TIMEZONE, TryTriConsumer.of(ValueWriter::setTime));
defaultWriters.put(TIMESTAMP, TryTriConsumer.of(ValueWriter::setTimestamp));
defaultWriters.put(TIMESTAMP_WITH_TIMEZONE, TryTriConsumer.of(ValueWriter::setTimestamp));
defaultWriters.put(TINYINT, TryTriConsumer.of(ValueWriter::setByte));
defaultWriters.put(VARBINARY, TryTriConsumer.of(ValueWriter::setBytes));
defaultWriters.put(VARCHAR, TryTriConsumer.of(ValueWriter::setString));
}
private static TryBiFunction longVarBinaryReader(int bufferSize, TryBiFunction binaryStreamProvider) {
return (input, index) -> {
try (InputStream is = binaryStreamProvider.apply(input, index)) {
if (null == is) return null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[max(DEFAULT_BUFFER_SIZE, bufferSize)];
int length, offset = 0;
while ((length = is.read(buffer)) != -1) {
bos.write(buffer, offset, length);
offset += length;
}
return bos.toByteArray();
} catch (Throwable t) {
throw new SQLException(t);
}
};
}
private static TryBiFunction clobReader(TryBiFunction binaryStreamProvider) {
return (input, index) -> {
C clob = binaryStreamProvider.apply(input, index);
return null == clob ? null : clob.getCharacterStream();
};
}
private static TryBiFunction longVarCharReader(TryBiFunction toReader) {
return (input, index) -> {
try (Reader r = toReader.apply(input, index)) {
return null == r ? null : new BufferedReader(r).lines().collect(joining());
} catch (Throwable t) {
throw new SQLException(t);
}
};
}
private static void setObject(ValueWriter writer, int index, Object value) throws SQLException {
if (value == null) writer.setObject(index, null);
else {
Class> cls = value.getClass();
if (Clob.class.isAssignableFrom(cls)) writer.setClob(index, (Clob) value);
else if (Blob.class.isAssignableFrom(cls)) writer.setBlob(index, (Blob) value);
else if (Time.class.isAssignableFrom(cls)) writer.setTime(index, (Time) value, Calendar.getInstance());
else if (Timestamp.class.isAssignableFrom(cls)) writer.setTimestamp(index, (Timestamp) value, Calendar.getInstance());
else if (java.sql.Date.class.isAssignableFrom(cls)) writer.setDate(index, (Date) value, Calendar.getInstance());
else if (java.util.Date.class.isAssignableFrom(cls)) writer.setTimestamp(index, new Timestamp(((java.util.Date) value).getTime()), Calendar.getInstance());
else if (Calendar.class.isAssignableFrom(cls)) writer.setTimestamp(index, new Timestamp(((Calendar) value).getTimeInMillis()));
else if (Instant.class.isAssignableFrom(cls)) writer.setTimestamp(index, new Timestamp(((Instant) value).toEpochMilli()), Calendar.getInstance());
else if (ZonedDateTime.class.isAssignableFrom(cls)) writer.setTimestamp(index, new Timestamp(((ZonedDateTime) value).toInstant().toEpochMilli()), Calendar.getInstance());
else writer.setObject(index, value);
}
}
@SuppressWarnings("unchecked")
static TryBiFunction reader(SQLType type) {
return (TryBiFunction) defaultReaders.getOrDefault(type, ValueReader::getObject);
}
@SuppressWarnings("unchecked")
static TryTriConsumer writer(SQLType type) {
return (TryTriConsumer) defaultWriters.getOrDefault(type, JDBCDefaults::setObject);
}
}