Skip to content

Commit

Permalink
add caches for mapped interface details and for current result set me…
Browse files Browse the repository at this point in the history
…tadata
  • Loading branch information
davidmoten committed Jun 3, 2015
1 parent 620b829 commit ab60350
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 58 deletions.
42 changes: 42 additions & 0 deletions src/main/java/com/github/davidmoten/rx/jdbc/AutoMapCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.github.davidmoten.rx.jdbc;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import com.github.davidmoten.rx.jdbc.Util.Col;
import com.github.davidmoten.rx.jdbc.Util.IndexedCol;
import com.github.davidmoten.rx.jdbc.Util.NamedCol;
import com.github.davidmoten.rx.jdbc.annotations.Column;
import com.github.davidmoten.rx.jdbc.annotations.Index;

class AutoMapCache {
final Map<String, Col> methodCols;
public Class<?> cls;

AutoMapCache(Class<?> cls) {
this.cls = cls;
this.methodCols = getMethodCols(cls);
}

private static Map<String, Col> getMethodCols(Class<?> cls) {
Map<String, Col> methodCols = new HashMap<String, Col>();
for (Method method : cls.getMethods()) {
String name = method.getName();
Column column = method.getAnnotation(Column.class);
if (column != null) {
//TODO check method has no params and has a mappable return type
methodCols.put(name, new NamedCol(column.value(), method.getReturnType()));
} else {
Index index = method.getAnnotation(Index.class);
if (index != null) {
//TODO check method has no params and has a mappable return type
methodCols.put(name, new IndexedCol(index.value(), method.getReturnType()));
}
}
}
return methodCols;
}


}
6 changes: 6 additions & 0 deletions src/main/java/com/github/davidmoten/rx/jdbc/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ final public class Database {
private final ThreadLocal<ConnectionProvider> currentConnectionProvider = new ThreadLocal<ConnectionProvider>();

private final ThreadLocal<Boolean> isTransactionOpen = new ThreadLocal<Boolean>();

static final ThreadLocal<ResultSetCache> rsCache = new ThreadLocal<ResultSetCache>();

static final ThreadLocal<AutoMapCache> autoMapCache = new ThreadLocal<AutoMapCache>();

/**
* Records the result of the last finished transaction (committed =
Expand Down Expand Up @@ -584,6 +588,7 @@ void beginTransactionSubscribe() {
void endTransactionSubscribe() {
log.debug("endTransactionSubscribe");
currentSchedulerFactory.set(null);
rsCache.set(null);
}

/**
Expand All @@ -593,6 +598,7 @@ void endTransactionObserve() {
log.debug("endTransactionObserve");
currentConnectionProvider.set(cp);
isTransactionOpen.set(false);
rsCache.set(null);
}

/**
Expand Down
27 changes: 20 additions & 7 deletions src/main/java/com/github/davidmoten/rx/jdbc/ResultSetCache.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
package com.github.davidmoten.rx.jdbc;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import com.github.davidmoten.rx.jdbc.Util.Col;

class ResultSetCache {

final ResultSet rs;
final Map<String, Integer> colIndexes;
final Map<String, Col> methodCols;

ResultSetCache(ResultSet rs, Map<String, Integer> colIndexes, Map<String, Col> methodCols) {

ResultSetCache(ResultSet rs) {
this.rs = rs;
this.colIndexes = colIndexes;
this.methodCols = methodCols;

this.colIndexes = collectColIndexes(rs);
}

private static Map<String, Integer> collectColIndexes(ResultSet rs) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
try {
ResultSetMetaData metadata = rs.getMetaData();
for (int i=1;i<=metadata.getColumnCount();i++) {
map.put(metadata.getColumnName(i),i);
}
return map;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

}
80 changes: 29 additions & 51 deletions src/main/java/com/github/davidmoten/rx/jdbc/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -260,19 +260,18 @@ public T call(ResultSet rs) {
@SuppressWarnings("unchecked")
static <T> T autoMap(ResultSet rs, Class<T> 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<T>) c);
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<T>) c);
}
}
throw new RuntimeException("constructor with number of parameters=" + n
+ " not found in " + cls);
}
throw new RuntimeException("constructor with number of parameters=" + n
+ " not found in " + cls);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
Expand All @@ -281,11 +280,11 @@ static <T> T autoMap(ResultSet rs, Class<T> cls) {
private static <T> T autoMapInterface(ResultSet rs, Class<T> cls) {
return ProxyService.newInstance(rs, cls);
}

static interface Col {
Class<?> returnType();
}

static class NamedCol implements Col {
final String name;
private final Class<?> returnType;
Expand Down Expand Up @@ -316,35 +315,35 @@ public Class<?> returnType() {
}
}


private static class ProxyService<T> implements java.lang.reflect.InvocationHandler {
Map<String, Object> values = new HashMap<String, Object>();
private final Map<String, Object> values = new HashMap<String, Object>();

ProxyService(ResultSet rs, Class<T> cls) {
//TODO cache this in ThreadLocal too
Map<String, Integer> colIndexes = new HashMap<String, Integer>();
try {
ResultSetMetaData metadata = rs.getMetaData();
for (int i=1;i<=metadata.getColumnCount();i++) {
colIndexes.put(metadata.getColumnName(i),i);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
//TODO cache methodCols in ThreadLocal
Map<String, Col> methodCols = getMethodCols(cls);
for (Method m: cls.getMethods()) {
// 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<String, Integer> 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<String, Col> 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);
int index;
if (column instanceof NamedCol) {
String name = ((NamedCol) column).name;
index = colIndexes.get(name.toUpperCase());
} else {
IndexedCol col = ((IndexedCol) column);
IndexedCol col = ((IndexedCol) column);
index = col.index;
}
Object value = autoMap(getObject(rs, column.returnType(), index), column.returnType());
Object value = autoMap(getObject(rs, column.returnType(), index),
column.returnType());
values.put(methodName, value);
}
}
Expand All @@ -360,27 +359,6 @@ public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
return values.get(m.getName());
}
}


private static Map<String, Col> getMethodCols(Class<?> cls) {
Map<String, Col> methodCols = new HashMap<String, Col>();
for (Method method : cls.getMethods()) {
String name = method.getName();
Column column = method.getAnnotation(Column.class);
if (column != null) {
//TODO check method has no params and has a mappable return type
methodCols.put(name, new NamedCol(column.value(), method.getReturnType()));
} else {
Index index = method.getAnnotation(Index.class);
if (index != null) {
//TODO check method has no params and has a mappable return type
methodCols.put(name, new IndexedCol(index.value(), method.getReturnType()));
}
}
}
return methodCols;
}


/**
* Converts the ResultSet column values into parameters to the given
Expand Down

0 comments on commit ab60350

Please sign in to comment.