add table convenience routines, clean code
This commit is contained in:
parent
625c8999a3
commit
1409d0e42d
20 changed files with 684 additions and 431 deletions
|
@ -15,12 +15,10 @@ public interface Config extends Function<String, String>, Supplier<Config> {
|
|||
*
|
||||
* @return a builder for specifying from where configuration should be loaded
|
||||
*/
|
||||
static ConfigFrom from() {
|
||||
return new ConfigFromImpl();
|
||||
static ConfigSupplier from() {
|
||||
return new ConfigSupplierImpl();
|
||||
}
|
||||
|
||||
// TODO add: String originalKey(String key) to find out the key before prefixing or other manipulation
|
||||
|
||||
/**
|
||||
* @return a trimmed, non-empty string, or null
|
||||
*/
|
||||
|
@ -47,59 +45,27 @@ public interface Config extends Function<String, String>, Supplier<Config> {
|
|||
default Config get() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
Integer getInteger(String key);
|
||||
|
||||
int getInteger(String key, int defaultValue);
|
||||
|
||||
/**
|
||||
* @throws ConfigMissingException if no value could be read for the specified key
|
||||
*/
|
||||
int getIntegerOrThrow(String key);
|
||||
|
||||
|
||||
Long getLong(String key);
|
||||
|
||||
long getLong(String key, long defaultValue);
|
||||
|
||||
/**
|
||||
* @throws ConfigMissingException if no value could be read for the specified key
|
||||
*/
|
||||
long getLongOrThrow(String key);
|
||||
|
||||
|
||||
Float getFloat(String key);
|
||||
|
||||
float getFloat(String key, float defaultValue);
|
||||
|
||||
/**
|
||||
* @throws ConfigMissingException if no value could be read for the specified key
|
||||
*/
|
||||
float getFloatOrThrow(String key);
|
||||
|
||||
|
||||
Double getDouble(String key);
|
||||
|
||||
double getDouble(String key, double defaultValue);
|
||||
|
||||
/**
|
||||
* @throws ConfigMissingException if no value could be read for the specified key
|
||||
*/
|
||||
double getDoubleOrThrow(String key);
|
||||
|
||||
|
||||
BigDecimal getBigDecimal(String key);
|
||||
|
||||
|
||||
BigDecimal getBigDecimal(String key, BigDecimal defaultValue);
|
||||
|
||||
/**
|
||||
* @throws ConfigMissingException if no value could be read for the specified key
|
||||
*/
|
||||
|
||||
BigDecimal getBigDecimalOrThrow(String key);
|
||||
|
||||
/**
|
||||
* Read a boolean value from the configuration. The value is not case-sensitivie,
|
||||
* and may be either true/false or yes/no. If no value was provided or an invalid
|
||||
|
@ -114,11 +80,6 @@ public interface Config extends Function<String, String>, Supplier<Config> {
|
|||
*/
|
||||
boolean getBooleanOrTrue(String key);
|
||||
|
||||
/**
|
||||
* @throws ConfigMissingException if no value could be read for the specified key
|
||||
*/
|
||||
boolean getBooleanOrThrow(String key);
|
||||
|
||||
/**
|
||||
* Show where configuration is coming from. This is useful to drop in your logs
|
||||
* for troubleshooting.
|
||||
|
|
|
@ -13,7 +13,7 @@ import java.util.logging.Logger;
|
|||
*/
|
||||
public class ConfigImpl implements Config {
|
||||
|
||||
private static final Logger log = Logger.getLogger(ConfigFromImpl.class.getName());
|
||||
private static final Logger log = Logger.getLogger(ConfigSupplierImpl.class.getName());
|
||||
|
||||
private final Function<String, String> provider;
|
||||
|
||||
|
@ -71,12 +71,6 @@ public class ConfigImpl implements Config {
|
|||
Integer value = getInteger(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntegerOrThrow(String key) {
|
||||
return nonnull(key, getInteger(key));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Long getLong(String key) {
|
||||
|
@ -97,12 +91,6 @@ public class ConfigImpl implements Config {
|
|||
Long value = getLong(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLongOrThrow(String key) {
|
||||
return nonnull(key, getLong(key));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Float getFloat(String key) {
|
||||
|
@ -123,12 +111,6 @@ public class ConfigImpl implements Config {
|
|||
Float value = getFloat(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloatOrThrow(String key) {
|
||||
return nonnull(key, getFloat(key));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Double getDouble(String key) {
|
||||
|
@ -149,12 +131,6 @@ public class ConfigImpl implements Config {
|
|||
Double value = getDouble(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDoubleOrThrow(String key) {
|
||||
return nonnull(key, getDouble(key));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(String key) {
|
||||
|
@ -176,11 +152,6 @@ public class ConfigImpl implements Config {
|
|||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimalOrThrow(String key) {
|
||||
return nonnull(key, getBigDecimal(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBooleanOrFalse(String key) {
|
||||
return parseBoolean(cleanString(key), false);
|
||||
|
@ -191,19 +162,6 @@ public class ConfigImpl implements Config {
|
|||
return parseBoolean(cleanString(key), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBooleanOrThrow(String key) {
|
||||
String value = nonnull(key, cleanString(key));
|
||||
value = value.toLowerCase();
|
||||
if (value.equals("yes") || value.equals("true")) {
|
||||
return true;
|
||||
}
|
||||
if (value.equals("no") || value.equals("false")) {
|
||||
return false;
|
||||
}
|
||||
throw new ConfigMissingException("Unrecognized boolean value for config key: " + key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sources() {
|
||||
return sources;
|
||||
|
|
|
@ -9,36 +9,36 @@ import java.util.function.Supplier;
|
|||
/**
|
||||
* Pull configuration properties from various sources and filter/manipulate them.
|
||||
*/
|
||||
public interface ConfigFrom extends Supplier<Config> {
|
||||
public interface ConfigSupplier extends Supplier<Config> {
|
||||
/**
|
||||
* Convenience method for fluent syntax.
|
||||
*
|
||||
* @return a builder for specifying from where configuration should be loaded
|
||||
*/
|
||||
static ConfigFrom firstOf() {
|
||||
return new ConfigFromImpl();
|
||||
static ConfigSupplier of() {
|
||||
return new ConfigSupplierImpl();
|
||||
}
|
||||
|
||||
static Config other(Function<String, String> other) {
|
||||
if (other instanceof Config) {
|
||||
return (Config) other;
|
||||
}
|
||||
return new ConfigFromImpl().custom(other::apply).get();
|
||||
return new ConfigSupplierImpl().custom(other::apply).get();
|
||||
}
|
||||
|
||||
ConfigFrom custom(Function<String, String> keyValueLookup);
|
||||
ConfigSupplier custom(Function<String, String> keyValueLookup);
|
||||
|
||||
ConfigFrom value(String key, String value);
|
||||
ConfigSupplier value(String key, String value);
|
||||
|
||||
ConfigFrom systemProperties();
|
||||
ConfigSupplier systemProperties();
|
||||
|
||||
ConfigFrom env();
|
||||
ConfigSupplier env();
|
||||
|
||||
ConfigFrom properties(Properties properties);
|
||||
ConfigSupplier properties(Properties properties);
|
||||
|
||||
ConfigFrom config(Config config);
|
||||
ConfigSupplier config(Config config);
|
||||
|
||||
ConfigFrom config(Supplier<Config> config);
|
||||
ConfigSupplier config(Supplier<Config> config);
|
||||
|
||||
/**
|
||||
* Adds a set of properties files to read from, which can be overridden by a system property "properties".
|
||||
|
@ -47,7 +47,7 @@ public interface ConfigFrom extends Supplier<Config> {
|
|||
* defaultPropertyFiles("properties", "conf/app.properties", "local.properties", "sample.properties")
|
||||
* </pre>
|
||||
*/
|
||||
ConfigFrom defaultProperties();
|
||||
ConfigSupplier defaultProperties();
|
||||
|
||||
/**
|
||||
* Adds a set of properties files to read from, which can be overridden by a specified system property.
|
||||
|
@ -56,7 +56,7 @@ public interface ConfigFrom extends Supplier<Config> {
|
|||
* defaultPropertyFiles(systemPropertyKey, Charset.defaultCharset().newDecoder(), filenames)
|
||||
* </pre>
|
||||
*/
|
||||
ConfigFrom defaultProperties(String systemPropertyKey, String... filenames);
|
||||
ConfigSupplier defaultProperties(String systemPropertyKey, String... filenames);
|
||||
|
||||
/**
|
||||
* Adds a set of properties files to read from, which can be overridden by a specified system property.
|
||||
|
@ -67,36 +67,35 @@ public interface ConfigFrom extends Supplier<Config> {
|
|||
* .split(File.pathSeparator));
|
||||
* </pre>
|
||||
*/
|
||||
ConfigFrom defaultProperties(String systemPropertyKey, CharsetDecoder decoder, String... filenames);
|
||||
ConfigSupplier defaultProperties(String systemPropertyKey, CharsetDecoder decoder, String... filenames);
|
||||
|
||||
ConfigFrom properties(String... filenames);
|
||||
ConfigSupplier properties(String... filenames);
|
||||
|
||||
ConfigFrom properties(CharsetDecoder decoder, String... filenames);
|
||||
ConfigSupplier properties(CharsetDecoder decoder, String... filenames);
|
||||
|
||||
ConfigFrom properties(InputStream... inputStreams);
|
||||
ConfigSupplier properties(InputStream... inputStreams);
|
||||
|
||||
ConfigFrom properties(CharsetDecoder decoder, InputStream... inputStreams);
|
||||
ConfigSupplier properties(CharsetDecoder decoder, InputStream... inputStreams);
|
||||
|
||||
ConfigFrom rename(String key, String newKey);
|
||||
ConfigSupplier rename(String key, String newKey);
|
||||
|
||||
ConfigFrom includeKeys(String... keys);
|
||||
ConfigSupplier includeKeys(String... keys);
|
||||
|
||||
ConfigFrom includePrefix(String... prefixes);
|
||||
ConfigSupplier includePrefix(String... prefixes);
|
||||
|
||||
ConfigFrom includeRegex(String regex);
|
||||
ConfigSupplier includeRegex(String regex);
|
||||
|
||||
ConfigFrom excludeKeys(String... keys);
|
||||
ConfigSupplier excludeKeys(String... keys);
|
||||
|
||||
ConfigFrom excludePrefix(String... prefixes);
|
||||
ConfigSupplier excludePrefix(String... prefixes);
|
||||
|
||||
ConfigFrom excludeRegex(String regex);
|
||||
ConfigSupplier excludeRegex(String regex);
|
||||
|
||||
ConfigFrom removePrefix(String... prefixes);
|
||||
ConfigSupplier removePrefix(String... prefixes);
|
||||
|
||||
ConfigFrom addPrefix(String prefix);
|
||||
ConfigSupplier addPrefix(String prefix);
|
||||
|
||||
ConfigFrom substitutions(Config config);
|
||||
ConfigSupplier substitutions(Config config);
|
||||
|
||||
Config get();
|
||||
|
||||
}
|
|
@ -15,8 +15,6 @@ import java.util.List;
|
|||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -24,86 +22,84 @@ import java.util.regex.Pattern;
|
|||
* Access configuration properties from a variety of standard sources,
|
||||
* and provide some basic filtering and mapping of property keys.
|
||||
*/
|
||||
public class ConfigFromImpl implements ConfigFrom {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ConfigFromImpl.class.getName());
|
||||
public class ConfigSupplierImpl implements ConfigSupplier {
|
||||
|
||||
private static final String SEPARATOR = FileSystems.getDefault().getSeparator();
|
||||
|
||||
private final List<Config> searchPath = new ArrayList<>();
|
||||
|
||||
public ConfigFromImpl() {
|
||||
public ConfigSupplierImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ConfigFromImpl(Config first) {
|
||||
public ConfigSupplierImpl(Config first) {
|
||||
searchPath.add(first);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom custom(Function<String, String> keyValueLookup) {
|
||||
public ConfigSupplier custom(Function<String, String> keyValueLookup) {
|
||||
return custom(keyValueLookup, "custom()");
|
||||
}
|
||||
|
||||
private ConfigFrom custom(Function<String, String> keyValueLookup, String source) {
|
||||
private ConfigSupplier custom(Function<String, String> keyValueLookup, String source) {
|
||||
searchPath.add(new ConfigImpl(keyValueLookup, source));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom value(String key, String value) {
|
||||
public ConfigSupplier value(String key, String value) {
|
||||
return custom(k -> k.equals(key) ? value : null, "value(" + key + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom config(Config config) {
|
||||
public ConfigSupplier config(Config config) {
|
||||
searchPath.add(config);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom config(Supplier<Config> config) {
|
||||
public ConfigSupplier config(Supplier<Config> config) {
|
||||
return config(config.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom systemProperties() {
|
||||
public ConfigSupplier systemProperties() {
|
||||
return custom(System::getProperty, "systemProperties()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom env() {
|
||||
public ConfigSupplier env() {
|
||||
return custom(System::getenv, "env()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom properties(Properties properties) {
|
||||
public ConfigSupplier properties(Properties properties) {
|
||||
return custom(properties::getProperty, "properties()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom defaultProperties() {
|
||||
public ConfigSupplier defaultProperties() {
|
||||
return defaultProperties("properties", "conf/app.properties", "local.properties", "sample.properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom defaultProperties(String systemPropertyKey, String... filenames) {
|
||||
public ConfigSupplier defaultProperties(String systemPropertyKey, String... filenames) {
|
||||
return defaultProperties(systemPropertyKey, Charset.defaultCharset().newDecoder(), filenames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom defaultProperties(String systemPropertyKey, CharsetDecoder decoder, String... filenames) {
|
||||
public ConfigSupplier defaultProperties(String systemPropertyKey, CharsetDecoder decoder, String... filenames) {
|
||||
String properties = System.getProperty(systemPropertyKey, String.join(SEPARATOR, filenames));
|
||||
return properties(Charset.defaultCharset().newDecoder(), properties.split(SEPARATOR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom properties(String... filenames) {
|
||||
public ConfigSupplier properties(String... filenames) {
|
||||
return properties(Charset.defaultCharset().newDecoder(), filenames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom properties(CharsetDecoder decoder, String... filenames) {
|
||||
public ConfigSupplier properties(CharsetDecoder decoder, String... filenames) {
|
||||
for (String filename : filenames) {
|
||||
if (filename != null) {
|
||||
Path path = Paths.get(filename);
|
||||
|
@ -122,12 +118,12 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom properties(InputStream... inputStreams) {
|
||||
public ConfigSupplier properties(InputStream... inputStreams) {
|
||||
return properties(Charset.defaultCharset().newDecoder(), inputStreams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom properties(CharsetDecoder decoder, InputStream... inputStreams) {
|
||||
public ConfigSupplier properties(CharsetDecoder decoder, InputStream... inputStreams) {
|
||||
for (InputStream inputStream : inputStreams) {
|
||||
Properties properties = new Properties();
|
||||
try (InputStreamReader reader = new InputStreamReader(inputStream, decoder)) {
|
||||
|
@ -141,8 +137,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom rename(String configKey, String newKey) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier rename(String configKey, String newKey) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
if (key.equals(configKey)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -154,8 +150,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom includeKeys(String... keys) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier includeKeys(String... keys) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
for (String k : keys) {
|
||||
if (key.equals(k)) {
|
||||
return lookup(key);
|
||||
|
@ -166,8 +162,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom includePrefix(String... prefixes) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier includePrefix(String... prefixes) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
for (String prefix : prefixes) {
|
||||
if (key.startsWith(prefix)) {
|
||||
return lookup(key);
|
||||
|
@ -178,8 +174,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom includeRegex(String regex) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier includeRegex(String regex) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
if (key.matches(regex)) {
|
||||
return lookup(key);
|
||||
}
|
||||
|
@ -188,8 +184,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom excludeKeys(String... keys) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier excludeKeys(String... keys) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
for (String k : keys) {
|
||||
if (key.equals(k)) {
|
||||
return null;
|
||||
|
@ -200,8 +196,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom excludePrefix(String... prefixes) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier excludePrefix(String... prefixes) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
for (String prefix : prefixes) {
|
||||
if (key.startsWith(prefix)) {
|
||||
return null;
|
||||
|
@ -212,8 +208,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom excludeRegex(String regex) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier excludeRegex(String regex) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
if (key.matches(regex)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -222,8 +218,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom removePrefix(String... prefixes) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier removePrefix(String... prefixes) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
// Give precedence to ones that already lacked the prefix,
|
||||
// do an include*() first if you don't want that
|
||||
String value = lookup(key);
|
||||
|
@ -242,8 +238,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom addPrefix(String prefix) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier addPrefix(String prefix) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
if (key.startsWith(prefix)) {
|
||||
return lookup(key.substring(prefix.length()));
|
||||
} else {
|
||||
|
@ -253,8 +249,8 @@ public class ConfigFromImpl implements ConfigFrom {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigFrom substitutions(Config config) {
|
||||
return new ConfigFromImpl(new ConfigImpl(key -> {
|
||||
public ConfigSupplier substitutions(Config config) {
|
||||
return new ConfigSupplierImpl(new ConfigImpl(key -> {
|
||||
String value = lookup(key);
|
||||
if (value != null) {
|
||||
// matches ${ENV_VAR_NAME} or $ENV_VAR_NAME
|
|
@ -2,6 +2,7 @@ package org.xbib.jdbc.query;
|
|||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -87,7 +88,7 @@ public interface Database extends Supplier<Database> {
|
|||
* Read the next value from a sequence. This method helps smooth over the
|
||||
* syntax differences across databases.
|
||||
*/
|
||||
Long nextSequenceValue( String sequenceName);
|
||||
Long nextSequenceValue(String sequenceName);
|
||||
|
||||
/**
|
||||
* Cause the underlying connection to commit its transaction immediately. This
|
||||
|
@ -159,7 +160,7 @@ public interface Database extends Supplier<Database> {
|
|||
* @param tableName the table to be checked
|
||||
* @return true if the table or view exists
|
||||
*/
|
||||
boolean tableExists( String tableName);
|
||||
boolean tableExists(String tableName);
|
||||
|
||||
/**
|
||||
* Convenience method to check whether a table or view exists or not.
|
||||
|
@ -171,7 +172,7 @@ public interface Database extends Supplier<Database> {
|
|||
* @param schemaName the schema expected to contain the table
|
||||
* @return true if the table or view exists
|
||||
*/
|
||||
boolean tableExists( String tableName, String schemaName);
|
||||
boolean tableExists(String tableName, String schemaName);
|
||||
|
||||
/**
|
||||
* Convenience method to get all column sizes from a table.
|
||||
|
@ -213,6 +214,32 @@ public interface Database extends Supplier<Database> {
|
|||
*/
|
||||
void assertTimeSynchronized();
|
||||
|
||||
Table getPagedRows(Table table);
|
||||
|
||||
long getRowsCount(Table table);
|
||||
|
||||
long countRows(String statement, Map<String, Object> params);
|
||||
|
||||
Table getSingleRow(String statement, Map<String, Object> params);
|
||||
|
||||
Table getUnlimitedRows(String statement, Map<String, Object> params);
|
||||
|
||||
Table getLimitedRows(String statement, Map<String, Object> params, int limit, int fetchSize, int timeoutSeconds);
|
||||
|
||||
void insert(String statement, Map<String, Object> params);
|
||||
|
||||
void insert(String statement, List<Map<String, Object>> params);
|
||||
|
||||
void update(String statement, Map<String, Object> params);
|
||||
|
||||
void update(String statement, List<Map<String, Object>> params);
|
||||
|
||||
void upsert(String insertStatement, String updateStatement, Map<String, Object> params);
|
||||
|
||||
void delete(String statement, Map<String, Object> params);
|
||||
|
||||
void delete(String statement, List<Map<String, Object>> params);
|
||||
|
||||
/**
|
||||
* Write data into a queue table, select a channel, and consume the returned primary keys.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.xbib.jdbc.query;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
|
@ -9,10 +10,15 @@ import java.sql.SQLException;
|
|||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
|
@ -276,6 +282,253 @@ public class DatabaseImpl implements Database {
|
|||
assertTimeSynchronized(10000, 30000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Table getPagedRows(Table table) {
|
||||
if (table == null) {
|
||||
return table;
|
||||
}
|
||||
Map<String, Object> params = table.getParams() != null ?
|
||||
new HashMap<>(table.getParams()) : new HashMap<>();
|
||||
if (table.getOffset() != null && table.getSize() != null) {
|
||||
params.put("offset", table.getOffset());
|
||||
params.put("limit", table.getSize());
|
||||
}
|
||||
String where = table.getWhereClause() != null ? table.getWhereClause() : "";
|
||||
String groupby = table.getGroupByClause() != null ? table.getGroupByClause() : "";
|
||||
String orderby = !table.getSort().isEmpty() ? "order by " + table.getSort() : "";
|
||||
String statement = table.getStatement() + " " + where + " " + groupby + " " + orderby;
|
||||
if (table.getOffset() != null && table.getSize() != null) {
|
||||
statement = statement + " offset :offset rows fetch next :limit rows only";
|
||||
}
|
||||
return getUnlimitedRows(statement, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRowsCount(Table table) {
|
||||
Map<String, Object> params = new HashMap<>(table.getParams());
|
||||
String statement = table.getStatement() + " " + table.getWhereClause();
|
||||
return countRows(statement, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countRows(String statement, Map<String, Object> params) {
|
||||
String countStatament = "select count(*) as \"cnt\" from (" + statement + ")";
|
||||
Table table = getSingleRow(countStatament, params);
|
||||
if (!table.isEmpty()) {
|
||||
BigDecimal bigDecimal = table.getValue(0,"cnt");
|
||||
return bigDecimal.longValue();
|
||||
} else {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Table getSingleRow(String statement, Map<String, Object> params) {
|
||||
return getLimitedRows(statement, params, 1, options().fetchSize(), options().timeoutSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Table getUnlimitedRows(String statement, Map<String, Object> params) {
|
||||
return getLimitedRows(statement, params, 0, options().fetchSize(), options().timeoutSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Table getLimitedRows(String statement, Map<String, Object> params,
|
||||
int limit, int fetchSize, int timeoutSeconds) {
|
||||
SqlSelect sql = toSelect(statement).fetchSize(fetchSize).withTimeoutSeconds(timeoutSeconds);
|
||||
selectParams(sql, params);
|
||||
Table table = new Table();
|
||||
sql.query(rows -> {
|
||||
ResultSetMetaData md = rows.getMetadata();
|
||||
List<Object> columnNames = new ArrayList<>();
|
||||
List<Object> classNames = new ArrayList<>();
|
||||
for (int i = 1; i <= md.getColumnCount(); i++) {
|
||||
columnNames.add(md.getColumnName(i).toLowerCase(Locale.ROOT));
|
||||
classNames.add(md.getColumnClassName(i));
|
||||
}
|
||||
table.add(columnNames);
|
||||
table.add(classNames);
|
||||
int i = 0;
|
||||
while (rows.next() && (limit <= 0 || i++ < limit)) {
|
||||
table.add(getRow(rows, classNames));
|
||||
}
|
||||
table.setTotal(rows.rowCount());
|
||||
return true;
|
||||
});
|
||||
return table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(String statement, Map<String, Object> params) {
|
||||
SqlInsert sql = toInsert(statement);
|
||||
insertParams(sql, params);
|
||||
sql.insert(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(String statement, List<Map<String, Object>> params) {
|
||||
SqlInsert sqlInsert = toInsert(statement);
|
||||
for (Map<String, Object> param : params) {
|
||||
insertParams(sqlInsert, param);
|
||||
sqlInsert.batch();
|
||||
}
|
||||
sqlInsert.insertBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String statement, Map<String, Object> params) {
|
||||
SqlUpdate sql = toUpdate(statement);
|
||||
updateParams(sql, params);
|
||||
sql.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String statement, List<Map<String, Object>> params) {
|
||||
SqlUpdate sqlUpdate = toUpdate(statement);
|
||||
for (Map<String, Object> param : params) {
|
||||
updateParams(sqlUpdate, param);
|
||||
sqlUpdate.update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upsert(String insertStatement, String updateStatement, Map<String, Object> params) {
|
||||
// try insert then update if error
|
||||
try {
|
||||
SqlInsert sql = toInsert(insertStatement);
|
||||
insertParams(sql, params);
|
||||
sql.insert(1);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, e.getMessage(), e);
|
||||
SqlUpdate sql = toUpdate(updateStatement);
|
||||
updateParams(sql, params);
|
||||
sql.update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String statement, Map<String, Object> params) {
|
||||
SqlUpdate sql = toDelete(statement);
|
||||
updateParams(sql, params);
|
||||
sql.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String statement, List<Map<String, Object>> params) {
|
||||
SqlUpdate sqlUpdate = toDelete(statement);
|
||||
for (Map<String, Object> param : params) {
|
||||
updateParams(sqlUpdate, param);
|
||||
sqlUpdate.update();
|
||||
}
|
||||
}
|
||||
|
||||
private void selectParams(SqlSelect sql, Map<String, Object> params) {
|
||||
if (params == null) {
|
||||
return;
|
||||
}
|
||||
params.forEach((k, v) -> {
|
||||
if (v instanceof String) {
|
||||
sql.argString(k, (String) v);
|
||||
} else if (v instanceof Integer) {
|
||||
sql.argInteger(k, (Integer) v);
|
||||
} else if (v instanceof Long) {
|
||||
sql.argLong(k, (Long) v);
|
||||
} else if (v instanceof Boolean) {
|
||||
sql.argBoolean(k, (Boolean) v);
|
||||
} else if (v instanceof LocalDate) {
|
||||
sql.argLocalDate(k, (LocalDate) v);
|
||||
} else if (v instanceof LocalDateTime) {
|
||||
sql.argLocalDateTime(k, (LocalDateTime) v);
|
||||
} else {
|
||||
throw new DatabaseException("unknown type for param: " + (v != null ? v.getClass() : "null"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void insertParams(SqlInsert sql, Map<String, Object> params) {
|
||||
if (params == null) {
|
||||
return;
|
||||
}
|
||||
params.forEach((k, v) -> {
|
||||
if (v instanceof String) {
|
||||
sql.argString(k, (String) v);
|
||||
} else if (v instanceof Integer) {
|
||||
sql.argInteger(k, (Integer) v);
|
||||
} else if (v instanceof Long) {
|
||||
sql.argLong(k, (Long) v);
|
||||
} else if (v instanceof Boolean) {
|
||||
sql.argBoolean(k, (Boolean) v);
|
||||
} else if (v instanceof LocalDate) {
|
||||
sql.argLocalDate(k, (LocalDate) v);
|
||||
} else if (v instanceof LocalDateTime) {
|
||||
sql.argLocalDateTime(k, (LocalDateTime) v);
|
||||
} else {
|
||||
throw new DatabaseException("unknown type for param: " + (v != null ? v.getClass() : "null"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateParams(SqlUpdate sql, Map<String, Object> params) {
|
||||
if (params == null) {
|
||||
return;
|
||||
}
|
||||
params.forEach((k, v) -> {
|
||||
if (v instanceof String) {
|
||||
sql.argString(k, (String) v);
|
||||
} else if (v instanceof Integer) {
|
||||
sql.argInteger(k, (Integer) v);
|
||||
} else if (v instanceof Long) {
|
||||
sql.argLong(k, (Long) v);
|
||||
} else if (v instanceof Boolean) {
|
||||
sql.argBoolean(k, (Boolean) v);
|
||||
} else if (v instanceof LocalDate) {
|
||||
sql.argLocalDate(k, (LocalDate) v);
|
||||
} else if (v instanceof LocalDateTime) {
|
||||
sql.argLocalDateTime(k, (LocalDateTime) v);
|
||||
} else {
|
||||
throw new DatabaseException("unknown type for param: " + (v != null ? v.getClass() : "null"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<Object> getRow(Rows rows, List<Object> classNames) {
|
||||
List<Object> row = new ArrayList<>();
|
||||
for (int i = 0; i < classNames.size(); i++) {
|
||||
String className = classNames.get(i).toString();
|
||||
switch (className) {
|
||||
case "java.lang.String":
|
||||
row.add(rows.getStringOrEmpty(i + 1));
|
||||
break;
|
||||
case "java.lang.Integer":
|
||||
row.add(rows.getIntegerOrNull(i + 1));
|
||||
break;
|
||||
case "java.lang.Long":
|
||||
row.add(rows.getLongOrNull(i + 1));
|
||||
break;
|
||||
case "java.lang.Boolean":
|
||||
row.add(rows.getBooleanOrFalse(i + 1));
|
||||
break;
|
||||
case "java.sql.Clob":
|
||||
case "oracle.jdbc.OracleClob":
|
||||
row.add(rows.getClobStringOrEmpty(i + 1));
|
||||
break;
|
||||
case "java.sql.Date":
|
||||
row.add(rows.getLocalDateOrNull(i + 1));
|
||||
break;
|
||||
case "java.sql.Timestamp":
|
||||
case "oracle.sql.TIMESTAMP":
|
||||
row.add(rows.getLocalDateTimeOrNull(i + 1));
|
||||
break;
|
||||
case "java.math.BigDecimal":
|
||||
row.add(rows.getBigDecimalOrNull(i + 1));
|
||||
break;
|
||||
default:
|
||||
throw new DatabaseException("unexpected column class name: " + className);
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeQueue(String table, String channel, String data, Consumer<Long> consumer) throws SQLException {
|
||||
writeNextIntoQueue(connection, table, channel, data, consumer);
|
||||
|
|
|
@ -29,7 +29,6 @@ public class MixedParameterSql {
|
|||
if (nameToArg == null) {
|
||||
nameToArg = new HashMap<>();
|
||||
}
|
||||
|
||||
StringBuilder newSql = new StringBuilder(sql.length());
|
||||
List<String> argNamesList = new ArrayList<>();
|
||||
List<String> rewrittenArgs = new ArrayList<>();
|
||||
|
@ -39,26 +38,19 @@ public class MixedParameterSql {
|
|||
while (searchIndex < sql.length()) {
|
||||
int nextColonIndex = sql.indexOf(':', searchIndex);
|
||||
int nextQmIndex = sql.indexOf('?', searchIndex);
|
||||
|
||||
if (nextColonIndex < 0 && nextQmIndex < 0) {
|
||||
newSql.append(sql.substring(searchIndex));
|
||||
break;
|
||||
}
|
||||
|
||||
if (nextColonIndex >= 0 && (nextQmIndex == -1 || nextColonIndex < nextQmIndex)) {
|
||||
// The next parameter we found is a named parameter (":foo")
|
||||
if (nextColonIndex > sql.length() - 2) {
|
||||
// Probably illegal sql, but handle boundary condition
|
||||
break;
|
||||
}
|
||||
|
||||
// Allow :: as escape for :
|
||||
if (sql.charAt(nextColonIndex + 1) == ':') {
|
||||
newSql.append(sql, searchIndex, nextColonIndex + 1);
|
||||
searchIndex = nextColonIndex + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
int endOfNameIndex = nextColonIndex + 1;
|
||||
while (endOfNameIndex < sql.length() && Character.isJavaIdentifierPart(sql.charAt(endOfNameIndex))) {
|
||||
endOfNameIndex++;
|
||||
|
@ -83,15 +75,11 @@ public class MixedParameterSql {
|
|||
}
|
||||
searchIndex = endOfNameIndex;
|
||||
} else {
|
||||
// The next parameter we found is a positional parameter ("?")
|
||||
|
||||
// Allow ?? as escape for ?
|
||||
if (nextQmIndex < sql.length() - 1 && sql.charAt(nextQmIndex + 1) == '?') {
|
||||
newSql.append(sql, searchIndex, nextQmIndex + 1);
|
||||
searchIndex = nextQmIndex + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
newSql.append(sql, searchIndex, nextQmIndex);
|
||||
if (currentPositionalArg >= positionalArgs.size()) {
|
||||
throw new DatabaseException("Not enough positional parameters (" + positionalArgs.size() + ") were provided");
|
||||
|
@ -108,8 +96,6 @@ public class MixedParameterSql {
|
|||
}
|
||||
this.sqlToExecute = newSql.toString();
|
||||
args = argsList.toArray(new Object[argsList.size()]);
|
||||
|
||||
// Sanity check number of arguments to provide a better error message
|
||||
if (currentPositionalArg != positionalArgs.size()) {
|
||||
throw new DatabaseException("Wrong number of positional parameters were provided (expected: "
|
||||
+ currentPositionalArg + ", actual: " + positionalArgs.size() + ")");
|
||||
|
@ -131,5 +117,4 @@ public class MixedParameterSql {
|
|||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,9 +4,14 @@ package org.xbib.jdbc.query;
|
|||
* Control various optional behavior for the database interactions.
|
||||
*/
|
||||
public interface Options {
|
||||
|
||||
int fetchSize();
|
||||
|
||||
int timeoutSeconds();
|
||||
|
||||
/**
|
||||
* Control whether the Database object will allow calls to commitNow()
|
||||
* and rollbackNow(). By default it will throw exceptions if you try to
|
||||
* Control whether the Database object will allow calls to commit()
|
||||
* and rollback(). By default it will throw exceptions if you try to
|
||||
* call those.
|
||||
*/
|
||||
boolean allowTransactionControl();
|
||||
|
|
|
@ -13,6 +13,16 @@ public class OptionsDefault implements Options {
|
|||
this.flavor = flavor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fetchSize() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int timeoutSeconds() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowTransactionControl() {
|
||||
return false;
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.xbib.jdbc.query;
|
|||
|
||||
import org.xbib.jdbc.query.flavor.Postgresql;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for selectively overriding another Options object.
|
||||
*/
|
||||
|
@ -41,6 +40,16 @@ public class OptionsOverride implements Options {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fetchSize() {
|
||||
return parent.fetchSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int timeoutSeconds() {
|
||||
return parent.timeoutSeconds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowTransactionControl() {
|
||||
return parent.allowTransactionControl();
|
||||
|
|
|
@ -16,7 +16,7 @@ import java.time.ZoneId;
|
|||
/**
|
||||
* Safely wrap a ResultSet and provide access to the data it contains.
|
||||
*/
|
||||
class RowsAdaptor implements Rows {
|
||||
class RowsAdapter implements Rows {
|
||||
|
||||
private final ResultSet rs;
|
||||
|
||||
|
@ -24,7 +24,7 @@ class RowsAdaptor implements Rows {
|
|||
|
||||
private int column = 1;
|
||||
|
||||
public RowsAdaptor(ResultSet rs, Options options) {
|
||||
public RowsAdapter(ResultSet rs, Options options) {
|
||||
this.rs = rs;
|
||||
this.options = options;
|
||||
}
|
||||
|
@ -71,7 +71,16 @@ class RowsAdaptor implements Rows {
|
|||
public Boolean getBooleanOrNull(int columnOneBased) {
|
||||
try {
|
||||
column = columnOneBased + 1;
|
||||
return toBoolean(rs, columnOneBased);
|
||||
String val = rs.getString(columnOneBased);
|
||||
if (val == null) {
|
||||
return null;
|
||||
} else if (val.equals("Y") || val.equals("1")) {
|
||||
return Boolean.TRUE;
|
||||
} else if (val.equals("N") || val.equals("0")) {
|
||||
return Boolean.FALSE;
|
||||
} else {
|
||||
throw new DatabaseException("Reading boolean from column " + columnOneBased + " but the value was not 'Y' or 'N'");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
@ -81,7 +90,16 @@ class RowsAdaptor implements Rows {
|
|||
public Boolean getBooleanOrNull(String columnName) {
|
||||
try {
|
||||
column = rs.findColumn(columnName) + 1;
|
||||
return toBoolean(rs, columnName);
|
||||
String val = rs.getString(columnName);
|
||||
if (val == null) {
|
||||
return null;
|
||||
} else if (val.equals("Y") || val.equals("1")) {
|
||||
return Boolean.TRUE;
|
||||
} else if (val.equals("N") || val.equals("0")) {
|
||||
return Boolean.FALSE;
|
||||
} else {
|
||||
throw new DatabaseException("Reading boolean from column \"" + columnName + "\" but the value was not 'Y' or 'N'");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
@ -133,7 +151,6 @@ class RowsAdaptor implements Rows {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer getIntegerOrNull() {
|
||||
return getIntegerOrNull(column++);
|
||||
|
@ -143,7 +160,8 @@ class RowsAdaptor implements Rows {
|
|||
public Integer getIntegerOrNull(int columnOneBased) {
|
||||
try {
|
||||
column = columnOneBased + 1;
|
||||
return toInteger(rs, columnOneBased);
|
||||
int val = rs.getInt(columnOneBased);
|
||||
return rs.wasNull() ? null : val;
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
@ -153,7 +171,8 @@ class RowsAdaptor implements Rows {
|
|||
public Integer getIntegerOrNull(String columnName) {
|
||||
try {
|
||||
column = rs.findColumn(columnName) + 1;
|
||||
return toInteger(rs, columnName);
|
||||
int val = rs.getInt(columnName);
|
||||
return rs.wasNull() ? null : val;
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
@ -181,7 +200,6 @@ class RowsAdaptor implements Rows {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Long getLongOrNull() {
|
||||
|
@ -192,7 +210,8 @@ class RowsAdaptor implements Rows {
|
|||
public Long getLongOrNull(int columnOneBased) {
|
||||
try {
|
||||
column = columnOneBased + 1;
|
||||
return toLong(rs, columnOneBased);
|
||||
long val = rs.getLong(columnOneBased);
|
||||
return rs.wasNull() ? null : val;
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
@ -202,7 +221,8 @@ class RowsAdaptor implements Rows {
|
|||
public Long getLongOrNull(String columnName) {
|
||||
try {
|
||||
column = rs.findColumn(columnName) + 1;
|
||||
return toLong(rs, columnName);
|
||||
long val = rs.getLong(columnName);
|
||||
return rs.wasNull() ? null : val;
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
@ -230,7 +250,6 @@ class RowsAdaptor implements Rows {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Float getFloatOrNull() {
|
||||
|
@ -241,7 +260,8 @@ class RowsAdaptor implements Rows {
|
|||
public Float getFloatOrNull(int columnOneBased) {
|
||||
try {
|
||||
column = columnOneBased + 1;
|
||||
return toFloat(rs, columnOneBased);
|
||||
float val = rs.getFloat(columnOneBased);
|
||||
return rs.wasNull() ? null : val;
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
@ -251,7 +271,8 @@ class RowsAdaptor implements Rows {
|
|||
public Float getFloatOrNull(String columnName) {
|
||||
try {
|
||||
column = rs.findColumn(columnName) + 1;
|
||||
return toFloat(rs, columnName);
|
||||
float val = rs.getFloat(columnName);
|
||||
return rs.wasNull() ? null : val;
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
@ -279,7 +300,6 @@ class RowsAdaptor implements Rows {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||