Skip to content

Commit

Permalink
Merge pull request #375 from aaberg/rewrite-reflection-and-add-record…
Browse files Browse the repository at this point in the history
…-support

Rewrite reflection and add record support
  • Loading branch information
aaberg authored Nov 3, 2024
2 parents d8a7a8b + 4956fb3 commit 3e5a6e5
Show file tree
Hide file tree
Showing 69 changed files with 760 additions and 1,699 deletions.
10 changes: 0 additions & 10 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,6 @@ jobs:
run: java -version
- name: Test with Maven java 17
run: mvn test
- name: Setup JDK 11 for testing
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Print java version
run: java -version
- name: Test with Maven java 11
run: mvn test

# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph
Expand Down
8 changes: 4 additions & 4 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<parent>
<groupId>org.sql2o</groupId>
<artifactId>sql2o-parent</artifactId>
<version>1.8.0-SNAPSHOT</version>
<version>1.9.0-SNAPSHOT</version>
</parent>
<artifactId>sql2o</artifactId>
<packaging>jar</packaging>
Expand Down Expand Up @@ -66,7 +66,7 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<version>5.11.0</version>
<scope>test</scope>
</dependency>

Expand Down Expand Up @@ -94,8 +94,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<source>17</source>
<target>17</target>
</configuration>
</plugin>

Expand Down
184 changes: 18 additions & 166 deletions core/src/main/java/org/sql2o/DefaultResultSetHandlerFactory.java
Original file line number Diff line number Diff line change
@@ -1,186 +1,38 @@
package org.sql2o;

import org.sql2o.converters.Converter;
import org.sql2o.converters.ConverterException;
import org.sql2o.quirks.Quirks;
import org.sql2o.reflection.Pojo;
import org.sql2o.reflection.PojoMetadata;
import org.sql2o.reflection.Setter;
import org.sql2o.tools.AbstractCache;

import java.sql.ResultSet;
import org.sql2o.reflection2.ObjectBuildableFactoryDelegate;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;


public class DefaultResultSetHandlerFactory<T> implements ResultSetHandlerFactory<T> {
private final PojoMetadata metadata;
private final Quirks quirks;
private final ObjectBuildableFactoryDelegate<T> objectBuilderDelegate;

public DefaultResultSetHandlerFactory(PojoMetadata pojoMetadata, Quirks quirks) {
this.metadata = pojoMetadata;
public DefaultResultSetHandlerFactory(ObjectBuildableFactoryDelegate<T> objectBuilderDelegate, Quirks quirks) {
this.objectBuilderDelegate = objectBuilderDelegate;
this.quirks = quirks;
}

@SuppressWarnings("unchecked")
private static Setter getSetter(
final Quirks quirks,
final String propertyPath,
final PojoMetadata metadata) {
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 = quirks.converterOf(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, metadata.isCaseSensitive(), obj);
pojo.setProperty(propertyPath, value, quirks);
}

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

private static class Key {
final String stringKey;
final DefaultResultSetHandlerFactory f;

DefaultResultSetHandlerFactory factory(){
return f;
}

private PojoMetadata getMetadata() {
return f.metadata;
}

private Quirks getQuirksMode() {
return f.quirks;
}

private Key(String stringKey, DefaultResultSetHandlerFactory f) {
this.stringKey = stringKey;
this.f = f;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

Key key = (Key) o;

return f.metadata.equals(key.getMetadata())
&& f.quirks == key.getQuirksMode()
&& stringKey.equals(key.stringKey);

}

@Override
public int hashCode() {
int result = f.metadata.hashCode();
result = 31 * result + f.quirks.hashCode();
result = 31 * result + stringKey.hashCode();
return result;
}
}


private static final AbstractCache<Key,ResultSetHandler,ResultSetMetaData>
c = new AbstractCache<Key, ResultSetHandler, ResultSetMetaData>() {
@Override
protected ResultSetHandler evaluate(Key key, ResultSetMetaData param) {
try {
return key.factory().newResultSetHandler0(param);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
};

@SuppressWarnings("unchecked")
public ResultSetHandler<T> newResultSetHandler(final ResultSetMetaData meta) throws SQLException {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 1; i <= meta.getColumnCount(); i++) {
stringBuilder.append(quirks.getColumnName(meta,i)).append("\n");
}
return c.get(new Key(stringBuilder.toString(), this),meta);
public ResultSetHandler<T> newResultSetHandler(final ResultSetMetaData meta) {
return resultSet -> {

}
final var objectBuilder = objectBuilderDelegate.newObjectBuilder();


@SuppressWarnings("unchecked")
private ResultSetHandler<T> newResultSetHandler0(final ResultSetMetaData meta) throws SQLException {
final Setter[] setters;
final Converter converter;
final boolean useExecuteScalar;
//TODO: it's possible to cache converter/setters/getters
// cache key is ResultSetMetadata + Bean type

converter = quirks.converterOf(metadata.getType());
final int columnCount = meta.getColumnCount();

setters = new Setter[columnCount + 1]; // setters[0] is always null
for (int i = 1; i <= columnCount; i++) {
String colName = quirks.getColumnName(meta, i);

setters[i] = getSetter(quirks, colName, metadata);

// If more than 1 column is fetched (we cannot fall back to executeScalar),
// and the setter doesn't exist, throw exception.
if (this.metadata.throwOnMappingFailure && setters[i] == null && columnCount > 1) {
throw new Sql2oException("Could not map " + colName + " to any property.");
}
}
/**
* Fallback to executeScalar if converter exists,
* we're selecting 1 column, and no property setter exists for the column.
*/
useExecuteScalar = converter != null && columnCount == 1 && setters[1] == null;
return new ResultSetHandler<T>() {
@SuppressWarnings("unchecked")
public T handle(ResultSet resultSet) throws SQLException {
if (useExecuteScalar) {
try {
return (T) converter.convert(quirks.getRSVal(resultSet, 1));
} catch (ConverterException e) {
throw new Sql2oException("Error occurred while converting value from database to type " + metadata.getType(), e);
}
for (int i = 1; i <= meta.getColumnCount(); i++) {
final var colName = quirks.getColumnName(meta, i);
try {
objectBuilder.withValue(colName, resultSet.getObject(i));
} catch (ReflectiveOperationException e) {
throw new Sql2oException("Error when trying to set value for column [" + colName + "]", e);
}

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

return (T) pojo;
}
try {
return objectBuilder.build();
} catch (ReflectiveOperationException e) {
throw new Sql2oException("Error occurred while creating object from ResultSet", e);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.sql2o;

import org.sql2o.quirks.Quirks;
import org.sql2o.reflection.PojoMetadata;
import org.sql2o.reflection2.ObjectBuildableFactory;

import java.util.Map;

Expand Down Expand Up @@ -54,12 +54,22 @@ public void setQuirks(Quirks quirks) {
this.quirks = quirks;
}



@SuppressWarnings("unchecked")
public <T> ResultSetHandlerFactory<T> newFactory(Class<T> clazz) {
PojoMetadata pojoMetadata = new PojoMetadata(clazz, caseSensitive, autoDeriveColumnNames, columnMappings, throwOnMappingError);
return new DefaultResultSetHandlerFactory(pojoMetadata, quirks);

return new DefaultResultSetHandlerFactory<>(() -> {
try {
return ObjectBuildableFactory.forClass(
clazz,
new Settings(
new NamingConvention(caseSensitive, autoDeriveColumnNames),
quirks,
throwOnMappingError),
getColumnMappings()
);
} catch (ReflectiveOperationException e) {
throw new Sql2oException("Error while trying to construct object from class " + clazz, e);
}
}, quirks);
}

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

import org.sql2o.tools.SnakeToCamelCase;

import java.util.Map;

public class NamingConvention {

private final boolean caseSensitive;
private final boolean autoDeriveColumnNames;

public NamingConvention(boolean caseSensitive, boolean autoDeriveColumnNames) {
this.caseSensitive = caseSensitive;
this.autoDeriveColumnNames = autoDeriveColumnNames;
}

public String deriveName(String name) {
var derivedName = name;

if (autoDeriveColumnNames) {
derivedName = SnakeToCamelCase.convert(derivedName);
}
if (!caseSensitive)
derivedName = derivedName.toLowerCase();

return derivedName;
}
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/sql2o/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.sql2o.logging.LocalLoggerFactory;
import org.sql2o.logging.Logger;
import org.sql2o.quirks.Quirks;
import org.sql2o.reflection.PojoIntrospector;
import org.sql2o.reflection2.PojoIntrospector;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
Expand Down
28 changes: 28 additions & 0 deletions core/src/main/java/org/sql2o/Settings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.sql2o;

import org.sql2o.quirks.Quirks;

public class Settings {

private final Quirks quirks;
private final NamingConvention namingConvention;
private final boolean throwOnMappingError;

public Settings(NamingConvention namingConvention, Quirks quirks, boolean throwOnMappingError) {
this.quirks = quirks;
this.namingConvention = namingConvention;
this.throwOnMappingError = throwOnMappingError;
}

public Quirks getQuirks() {
return quirks;
}

public NamingConvention getNamingConvention() {
return namingConvention;
}

public boolean isThrowOnMappingError(){
return throwOnMappingError;
}
}
13 changes: 3 additions & 10 deletions core/src/main/java/org/sql2o/converters/Convert.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
Expand Down Expand Up @@ -127,17 +125,12 @@ public static <E> Converter<E> throwIfNull(Class<E> clazz, Converter<E> converte
}

public static <E> Converter<E> getConverterIfExists(Class<E> clazz) {
Converter c;
rl.lock();
try {
c = registeredConverters.get(clazz);
} finally {
rl.unlock();
}
final var c = (Converter<E>)registeredConverters.get(clazz);

if (c != null) return c;

if (clazz.isEnum()) {
return registeredEnumConverterFactory.newConverter((Class) clazz);
return registeredEnumConverterFactory.newConverter((Class)clazz);
}
return null;
}
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/org/sql2o/converters/DefaultConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.sql2o.converters;

public class DefaultConverter extends ConverterBase<Object> {
@Override
public Object convert(Object val) throws ConverterException {
return val;
}
}
Loading

0 comments on commit 3e5a6e5

Please sign in to comment.