com.github.davidmoten.rx.jdbc.Util Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rxjava-jdbc Show documentation
Show all versions of rxjava-jdbc Show documentation
rx-java Observables for jdbc
package com.github.davidmoten.rx.jdbc;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.davidmoten.rx.jdbc.QuerySelect.Builder;
import com.github.davidmoten.rx.jdbc.exceptions.SQLRuntimeException;
import rx.functions.Func1;
/**
* Utility methods.
*/
public final class Util {
/**
* Private constructor to prevent instantiation.
*/
private Util() {
// prevent instantiation
}
/**
* Logger.
*/
private static final Logger log = LoggerFactory.getLogger(Util.class);
/**
* Count the number of JDBC parameters in a sql statement.
*
* @param query
* .sql()
* @return
*/
static int parametersCount(Query query) {
if (query.names().isEmpty())
return countQuestionMarkParameters(query.sql());
else
return query.names().size();
}
// Visible for testing
static int countQuestionMarkParameters(String sql) {
// was originally using regular expressions, but they didn't work well
// for ignoring parameter-like strings inside quotes.
int count = 0;
int length = sql.length();
boolean inSingleQuote = false;
boolean inDoubleQuote = false;
for (int i = 0; i < length; i++) {
char c = sql.charAt(i);
if (inSingleQuote) {
if (c == '\'') {
inSingleQuote = false;
}
} else if (inDoubleQuote) {
if (c == '"') {
inDoubleQuote = false;
}
} else {
if (c == '\'') {
inSingleQuote = true;
} else if (c == '"') {
inDoubleQuote = true;
} else if (c == '?') {
count++;
}
}
}
return count;
}
/**
* Cancels then closes a {@link PreparedStatement} and logs exceptions
* without throwing. Does nothing if ps is null.
*
* @param ps
*/
static void closeQuietly(PreparedStatement ps) {
try {
boolean isClosed;
try {
if (ps != null)
isClosed = ps.isClosed();
else
isClosed = true;
} catch (SQLException e) {
log.debug(e.getMessage());
isClosed = true;
}
if (ps != null && !isClosed) {
try {
ps.cancel();
log.debug("cancelled {}", ps);
} catch (SQLException e) {
log.debug(e.getMessage());
}
ps.close();
log.debug("closed {}", ps);
}
} catch (SQLException e) {
log.debug(e.getMessage(), e);
} catch (RuntimeException e) {
log.debug(e.getMessage(), e);
}
}
/**
* Closes a {@link Connection} and logs exceptions without throwing. Does
* nothing if connection is null.
*
* @param connection
*/
static void closeQuietly(Connection connection) {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
log.debug("closed {}", connection);
}
} catch (SQLException e) {
log.debug(e.getMessage(), e);
} catch (RuntimeException e) {
log.debug(e.getMessage(), e);
}
}
/**
* Closes a {@link Connection} only if the connection is in auto commit mode
* and logs exceptions without throwing. Does nothing if connection is null.
*
* @param connection
*/
static boolean closeQuietlyIfAutoCommit(Connection connection) {
try {
if (connection != null && !connection.isClosed() && connection.getAutoCommit()) {
closeQuietly(connection);
return true;
} else
return false;
} catch (SQLException e) {
throw new SQLRuntimeException(e);
}
}
/**
* Commits a {@link Connection} and logs exceptions without throwing.
*
* @param connection
*/
static void commit(Connection connection) {
if (connection != null)
try {
connection.commit();
log.debug("committed");
} catch (SQLException e) {
throw new SQLRuntimeException(e);
}
}
/**
* Rolls back a {@link Connection} and logs exceptions without throwing.
*
* @param connection
*/
static void rollback(Connection connection) {
if (connection != null)
try {
connection.rollback();
log.debug("rolled back");
} catch (SQLException e) {
throw new SQLRuntimeException(e);
}
}
/**
* Closes a {@link ResultSet} and logs exceptions without throwing.
*
* @param rs
*/
static void closeQuietly(ResultSet rs) {
try {
if (rs != null && !rs.isClosed()) {
rs.close();
log.debug("closed {}", rs);
}
} catch (SQLException e) {
log.debug(e.getMessage(), e);
} catch (RuntimeException e) {
log.debug(e.getMessage(), e);
}
}
/**
* Returns true if and only if {@link Connection} is in auto commit mode.
*
* @param con
* @return
*/
static boolean isAutoCommit(Connection con) {
try {
return con.getAutoCommit();
} catch (SQLException e) {
throw new SQLRuntimeException(e);
}
}
/**
* Returns the empty list whenever called.
*/
static Func1> TO_EMPTY_PARAMETER_LIST = new Func1>() {
@Override
public List call(Integer n) {
return Collections.emptyList();
};
};
/**
* Returns a function that converts the ResultSet column values into
* parameters to the constructor (with number of parameters equals the
* number of columns) of type cls
then returns an instance of
* type cls
. See {@link Builder#autoMap(Class)}.
*
* @param cls
* @return
*/
static ResultSetMapper autoMap(final Class cls) {
return new ResultSetMapper() {
@Override
public T call(ResultSet rs) {
return autoMap(rs, cls);
}
};
}
/**
* Converts the ResultSet column values into parameters to the constructor
* (with number of parameters equals the number of columns) of type
* T
then returns an instance of type T
. See See
* {@link Builder#autoMap(Class)}.
*
* @param cls
* the class of the resultant instance
* @return an automapped instance
*/
@SuppressWarnings("unchecked")
static T autoMap(ResultSet rs, Class cls) {
try {
if (cls.isInterface()) {
return autoMapInterface(rs, cls);
} else {
int n = rs.getMetaData().getColumnCount();
for (Constructor> c : cls.getDeclaredConstructors()) {
if (n == c.getParameterTypes().length) {
return autoMap(rs, (Constructor) c);
}
}
throw new RuntimeException(
"constructor with number of parameters=" + n + " not found in " + cls);
}
} catch (SQLException e) {
throw new SQLRuntimeException(e);
}
}
private static T autoMapInterface(ResultSet rs, Class cls) {
return ProxyService.newInstance(rs, cls);
}
static interface Col {
Class> returnType();
}
static class NamedCol implements Col {
final String name;
private final Class> returnType;
public NamedCol(String name, Class> returnType) {
this.name = name;
this.returnType = returnType;
}
@Override
public Class> returnType() {
return returnType;
}
@Override
public String toString() {
return "NamedCol [name=" + name + ", returnType=" + returnType + "]";
}
}
static class IndexedCol implements Col {
final int index;
private final Class> returnType;
public IndexedCol(int index, Class> returnType) {
this.index = index;
this.returnType = returnType;
}
@Override
public Class> returnType() {
return returnType;
}
@Override
public String toString() {
return "IndexedCol [index=" + index + ", returnType=" + returnType + "]";
}
}
private static class ProxyService implements java.lang.reflect.InvocationHandler {
private final Map values = new HashMap();
ProxyService(ResultSet rs, Class cls) {
// load information from cache about the result set
if (Database.rsCache.get() == null || Database.rsCache.get().rs != rs)
Database.rsCache.set(new ResultSetCache(rs));
Map colIndexes = Database.rsCache.get().colIndexes;
// load information from cache about the class
if (Database.autoMapCache.get() == null || Database.autoMapCache.get().cls != cls)
Database.autoMapCache.set(new AutoMapCache(cls));
Map methodCols = Database.autoMapCache.get().methodCols;
// calculate values for all the interface methods and put them in a
// map
for (Method m : cls.getMethods()) {
String methodName = m.getName();
Col column = methodCols.get(methodName);
Integer index;
if (column instanceof NamedCol) {
String name = ((NamedCol) column).name;
index = colIndexes.get(name.toUpperCase());
if (index == null) {
throw new SQLRuntimeException(
"query column names do not include '" + name + "'");
}
} else {
IndexedCol col = ((IndexedCol) column);
index = col.index;
}
Object value = autoMap(getObject(rs, column.returnType(), index),
column.returnType());
values.put(methodName, value);
}
}
@SuppressWarnings("unchecked")
public static T newInstance(ResultSet rs, Class cls) {
return (T) java.lang.reflect.Proxy.newProxyInstance(cls.getClassLoader(),
new Class[] { cls }, new ProxyService(rs, cls));
}
@Override
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
return values.get(m.getName());
}
}
static String camelCaseToUnderscore(String camelCased) {
// guava has best solution for this with CaseFormat class
// but don't want to add dependency just for this method
final String regex = "([a-z])([A-Z]+)";
final String replacement = "$1_$2";
return camelCased.replaceAll(regex, replacement);
}
static String first(String[] value) {
if (value == null || value.length == 0)
return null;
else
return value[0];
}
/**
* Converts the ResultSet column values into parameters to the given
* constructor (with number of parameters equals the number of columns) of
* type T
then returns an instance of type T
. See
* See {@link Builder#autoMap(Class)}.
*
* @param rs
* the result set row
* @param c
* constructor to use for instantiation
* @return automapped instance
*/
private static T autoMap(ResultSet rs, Constructor c) {
Class>[] types = c.getParameterTypes();
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy