All Downloads are FREE. Search and download functionalities are using the official Maven repository.

buckelieg.jdbc.JDBCDefaults Maven / Gradle / Ivy

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);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy