Skip to content

Commit

Permalink
Merge branch 'master' into converters_v2
Browse files Browse the repository at this point in the history
Conflicts:
	core/src/main/java/org/sql2o/PojoResultSetIterator.java
  • Loading branch information
aaberg committed Apr 7, 2014
2 parents 4411293 + e3d3d93 commit 35d24ce
Show file tree
Hide file tree
Showing 22 changed files with 929 additions and 96 deletions.
3 changes: 2 additions & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9</version>
<version>2.9.1</version>
<executions>
<execution>
<id>attach-javadoc</id>
Expand All @@ -165,6 +165,7 @@
</executions>
<configuration>
<stylesheet>java</stylesheet>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</plugin>
</plugins>
Expand Down
133 changes: 97 additions & 36 deletions core/src/main/java/org/sql2o/PojoResultSetIterator.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import org.sql2o.quirks.Quirks;
import org.sql2o.reflection.Pojo;
import org.sql2o.reflection.PojoMetadata;
import org.sql2o.reflection.Setter;
import org.sql2o.tools.ResultSetUtils;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
Expand All @@ -18,53 +20,112 @@
* @author aldenquimby@gmail.com
*/
public class PojoResultSetIterator<T> extends ResultSetIteratorBase<T> {
private PojoMetadata metadata;
private Converter converter;
private boolean useExecuteScalar;
private ResultSetHandler<T> handler;

public PojoResultSetIterator(ResultSet rs, boolean isCaseSensitive, Quirks quirks, PojoMetadata metadata) {
super(rs, isCaseSensitive, quirks);

this.metadata = metadata;
this.converter = Convert.getConverterIfExists(metadata.getType());
this.useExecuteScalar = shouldTryExecuteScalar();
}

@SuppressWarnings("unchecked") // Convert should be refactored so that this isn't needed
@Override
protected T readNext() throws SQLException {
if (useExecuteScalar) {
try {
return (T)converter.convert(ResultSetUtils.getRSVal(rs, 1));
}
catch (ConverterException e) {
throw new Sql2oException("Error occurred while converting value from database to type " + metadata.getType(), e);
}
try {
this.handler = newResultSetHandler(rs.getMetaData(), isCaseSensitive, metadata);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

// otherwise we want executeAndFetch with object mapping
Pojo pojo = new Pojo(metadata, isCaseSensitive);
for(int colIdx = 1; colIdx <= meta.getColumnCount(); colIdx++) {
String colName = getColumnName(colIdx);
Object value = ResultSetUtils.getRSVal(rs, colIdx);
pojo.setProperty(colName, value);
private static Setter getSetter(
final String propertyPath,
final PojoMetadata metadata,
final boolean isCaseSensitive){
int index = propertyPath.indexOf('.');
if(index<=0){
// Simple path - fast way
final Setter setter = metadata.getPropertySetterIfExists(propertyPath);
// behavior change: do not throw if POJO contains less properties
if(setter==null) return null;
final Converter converter=Convert.getConverterIfExists(setter.getType());
// setter without converter
if(converter==null) return setter;
return new Setter() {
public void setProperty(Object obj, Object value) {
try {
setter.setProperty(obj, converter.convert( value ));
} catch (ConverterException e) {
throw new Sql2oException("Error trying to convert column " + propertyPath + " to type " + setter.getType(), e);
}
}
public Class getType() {
return setter.getType();
}
};
}
// dot path - long way
// i'm too lazy now to rewrite this case so I just call old unoptimized code...
// TODO: rewrite, get rid of POJO class
return new Setter() {
public void setProperty(Object obj, Object value) {
Pojo pojo = new Pojo(metadata, isCaseSensitive, obj);
pojo.setProperty(propertyPath, value);
}

return (T)pojo.getObject();
public Class getType() {
// doesn't used anyway
return Object.class;
}
};
}

/**
* Fallback to executeScalar if converter exists,
* we're selecting 1 column, and no property setter exists for the column.
*/
private boolean shouldTryExecuteScalar() {
private <T> ResultSetHandler<T> newResultSetHandler(final ResultSetMetaData meta, boolean isCaseSensitive, final PojoMetadata metadata){
final Setter[] setters;
final Converter converter;
final boolean useExecuteScalar;
//TODO: it's possible to cache converter/setters
// cache key is ResultSetMetadata + Bean type

converter = Convert.getConverterIfExists(metadata.getType());
try {
return converter != null &&
meta.getColumnCount() == 1 &&
metadata.getPropertySetterIfExists(getColumnName(1)) == null;
}
catch (SQLException ex) {
int colCount = meta.getColumnCount();
setters=new Setter[colCount+1]; // setters[0] is always null
for (int i = 1; i <= colCount; i++) {
String colName = getColumnName(i);
// behavior change: do not throw if POJO contains less properties
setters[i] = getSetter(colName, metadata, isCaseSensitive);
}
/**
* Fallback to executeScalar if converter exists,
* we're selecting 1 column, and no property setter exists for the column.
*/
useExecuteScalar = converter !=null && colCount==1 && setters[1]==null;
} catch (SQLException ex) {
throw new Sql2oException("Database error: " + ex.getMessage(), ex);
}
return new ResultSetHandler<T>() {
@SuppressWarnings("unchecked")
public T handle(ResultSet resultSet) throws SQLException {
if (useExecuteScalar) {
try {
return (T)converter.convert(ResultSetUtils.getRSVal(rs, 1));
}
catch (ConverterException e) {
throw new Sql2oException("Error occurred while converting value from database to type " + metadata.getType(), e);
}
}

// otherwise we want executeAndFetch with object mapping
Object pojo = metadata.getObjectConstructor().newInstance();
for(int colIdx = 1; colIdx <= meta.getColumnCount(); colIdx++) {
Setter setter = setters[colIdx];
if(setter==null) continue;
setter.setProperty(pojo,ResultSetUtils.getRSVal(rs, colIdx));
}

return (T)pojo;
}
};

}

@Override
protected T readNext() throws SQLException {
return handler.handle(rs);
}

}
13 changes: 13 additions & 0 deletions core/src/main/java/org/sql2o/ResultSetHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sql2o;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
* User: dimzon
* Date: 4/7/14
* Time: 12:01 AM
*/
public interface ResultSetHandler<T> {
T handle(ResultSet resultSet) throws SQLException;
}
13 changes: 13 additions & 0 deletions core/src/main/java/org/sql2o/ResultSetHandlerFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sql2o;

import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
* User: dimzon
* Date: 4/7/14
* Time: 12:02 AM
*/
public interface ResultSetHandlerFactory {
<T> ResultSetHandler<T> newResultSetHandler(Class<T> type, ResultSetMetaData resultSetMetaData) throws SQLException;
}
60 changes: 57 additions & 3 deletions core/src/main/java/org/sql2o/data/Row.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import org.sql2o.converters.*;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.*;

/**
* Represents a result set row.
Expand Down Expand Up @@ -149,4 +147,60 @@ public String getString(String columnName){
throw new Sql2oException("Could not convert column with name " + columnName+ " to " + String.class.getName());
}
}

/**
* View row as a simple map.
*/
public Map<String, Object> asMap()
{
return new Map<String, Object>() {
public int size() {
return values.size();
}

public boolean isEmpty() {
return values.isEmpty();
}

public boolean containsKey(Object key) {
return columnNameToIdxMap.containsKey(key);
}

public boolean containsValue(Object value) {
return values.containsValue(value);
}

public Object get(Object key) {
return values.get(columnNameToIdxMap.get(key));
}

public Object put(String key, Object value) {
throw new UnsupportedOperationException("Row map is immutable.");
}

public Object remove(Object key) {
throw new UnsupportedOperationException("Row map is immutable.");
}

public void putAll(Map<? extends String, ?> m) {
throw new UnsupportedOperationException("Row map is immutable.");
}

public void clear() {
throw new UnsupportedOperationException("Row map is immutable.");
}

public Set<String> keySet() {
return columnNameToIdxMap.keySet();
}

public Collection<Object> values() {
return values.values();
}

public Set<Entry<String, Object>> entrySet() {
throw new UnsupportedOperationException("Row map does not support entrySet.");
}
};
}
}
17 changes: 16 additions & 1 deletion core/src/main/java/org/sql2o/data/Table.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.sql2o.data;

import java.util.List;
import java.util.*;

/**
* Represents an offline result set with columns and rows and data.
Expand All @@ -27,4 +27,19 @@ public List<Row> rows() {
public List<Column> columns() {
return columns;
}

public List<Map<String, Object>> asList()
{
return new AbstractList<Map<String, Object>>() {
@Override
public Map<String, Object> get(int index) {
return rows.get(index).asMap();
}

@Override
public int size() {
return rows.size();
}
};
}
}
58 changes: 58 additions & 0 deletions core/src/main/java/org/sql2o/reflection/FactoryFacade.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.sql2o.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

@SuppressWarnings("Unsafe")
public class FactoryFacade {
private final static FactoryFacade instance;

static {
MethodSetterFactory m;
try {
m = (MethodSetterFactory) Class
.forName("org.sql2o.reflection.MethodAccessorsGenerator")
.newInstance();
} catch (Throwable ex) {
m = new ReflectionMethodSetterFactory();
}
FieldSetterFactory f;
ObjectConstructorFactory o;
try {
Class cls = Class.forName("org.sql2o.reflection.UnsafeFieldSetterFactory");
f = (FieldSetterFactory) cls.newInstance();
o = (ObjectConstructorFactory) f;
} catch (Throwable ex) {
f = new ReflectionFieldSetterFactory();
o = new ReflectionObjectConstructorFactory();
}
instance = new FactoryFacade(f, m, o);
}

private final FieldSetterFactory fieldSetterFactory;
private final MethodSetterFactory methodSetterFactory;
private final ObjectConstructorFactory objectConstructorFactory;

public FactoryFacade(FieldSetterFactory fieldSetterFactory, MethodSetterFactory methodSetterFactory, ObjectConstructorFactory objectConstructorFactory) {
this.fieldSetterFactory = fieldSetterFactory;
this.methodSetterFactory = methodSetterFactory;
this.objectConstructorFactory = objectConstructorFactory;
}

public static FactoryFacade getInstance() {
return instance;
}

public Setter newSetter(Field field) {
return fieldSetterFactory.newSetter(field);
}

public Setter newSetter(Method method) {
return methodSetterFactory.newSetter(method);
}

public ObjectConstructor newConstructor(Class<?> cls) {
return objectConstructorFactory.newConstructor(cls);
}
}

14 changes: 14 additions & 0 deletions core/src/main/java/org/sql2o/reflection/FieldSetterFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sql2o.reflection;

import java.lang.reflect.Field;

/**
* Created with IntelliJ IDEA.
* User: dimzon
* Date: 4/6/14
* Time: 12:39 AM
* To change this template use File | Settings | File Templates.
*/
public interface FieldSetterFactory {
Setter newSetter(Field field);
}
Loading

0 comments on commit 35d24ce

Please sign in to comment.