Add ResultSetIterable support to jdbcmapper

This commit is contained in:
Travis Burtrum 2017-06-09 00:36:41 -04:00
parent d74c272e73
commit 881de93799
4 changed files with 118 additions and 18 deletions

View File

@ -2,6 +2,7 @@ package com.moparisthebest.jdbc.codegen;
import com.moparisthebest.jdbc.Finishable;
import com.moparisthebest.jdbc.ResultSetMapper;
import com.moparisthebest.jdbc.util.ResultSetIterable;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
@ -26,7 +27,8 @@ import static com.moparisthebest.jdbc.codegen.JdbcMapperProcessor.typeMirrorToCl
public class CompileTimeResultSetMapper {
public final Types types;
public final TypeMirror collectionType, mapType, mapCollectionType, iteratorType, listIteratorType, finishableType, resultSetType;
public final TypeMirror collectionType, mapType, mapCollectionType, iteratorType, listIteratorType, finishableType, resultSetType, resultSetIterableType;
private final boolean java8 = false;
public CompileTimeResultSetMapper(final ProcessingEnvironment processingEnv) {
types = processingEnv.getTypeUtils();
@ -41,6 +43,8 @@ public class CompileTimeResultSetMapper {
finishableType = elements.getTypeElement(Finishable.class.getCanonicalName()).asType();
resultSetType = elements.getTypeElement(ResultSet.class.getCanonicalName()).asType();
resultSetIterableType = types.getDeclaredType(elements.getTypeElement(ResultSetIterable.class.getCanonicalName()), types.getWildcardType(null, null));
}
public static String getConcreteClassCanonicalName(final TypeMirror returnType, final Class defaultConcreteClass) {
@ -56,7 +60,11 @@ public class CompileTimeResultSetMapper {
return typeMirrorStringNoGenerics(returnType);
}
public void mapToResultType(final Writer w, final String[] keys, final ExecutableElement eeMethod, final MaxRows maxRows, final String cal, final String cleaner) throws IOException, NoSuchMethodException, ClassNotFoundException {
/**
*
* @return true if calling code should close rs (ResultSet) and ps (PreparedStatement) if closePs is false, false otherwise
*/
public boolean mapToResultType(final Writer w, final String[] keys, final ExecutableElement eeMethod, final MaxRows maxRows, final String cal, final String cleaner, final boolean closePs) throws IOException, NoSuchMethodException, ClassNotFoundException {
//final Method m = fromExecutableElement(eeMethod);
//final Class returnType = m.getReturnType();
final TypeMirror returnTypeMirror = eeMethod.getReturnType();
@ -79,18 +87,22 @@ public class CompileTimeResultSetMapper {
collectionTypeMirror,
componentTypeMirror,
maxRows, cal, cleaner);
return;
return true;
}
toMap(w, keys, returnTypeMirror, typeArguments.get(0), typeArguments.get(1), maxRows, cal, cleaner);
} else if (types.isAssignable(returnTypeMirror, iteratorType)) {
final List<? extends TypeMirror> typeArguments = ((DeclaredType) returnTypeMirror).getTypeArguments();
if (types.isAssignable(returnTypeMirror, listIteratorType))
if (types.isAssignable(returnTypeMirror, resultSetIterableType)) {
toResultSetIterable(w, keys, typeArguments.get(0), cal, cleaner, closePs);
return false;
} else if (types.isAssignable(returnTypeMirror, listIteratorType))
toListIterator(w, keys, typeArguments.get(0), maxRows, cal, cleaner);
else
toIterator(w, keys, typeArguments.get(0), maxRows, cal, cleaner);
} else {
toObject(w, keys, returnTypeMirror, cal, cleaner);
}
return true;
}
public CompileTimeRowToObjectMapper getRowMapper(final String[] keys, TypeMirror returnTypeClass, String cal, TypeMirror mapValType, TypeMirror mapKeyType) {
@ -109,6 +121,35 @@ public class CompileTimeResultSetMapper {
clean(w, cleaner).write(";\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n");
}
private void toResultSetIterable(final Writer w, final String[] keys, final TypeMirror returnTypeMirror, final String cal, final String cleaner, final boolean closePs) throws IOException, ClassNotFoundException {
w.write("\t\t\treturn com.moparisthebest.jdbc.util.ResultSetIterable.getResultSetIterable(rs,\n\t\t\t\t\trs.next() ? ");
if(java8) {
w.append("(rs, ").append(cal == null ? "_cal" : cal).append(") -> {\n");
} else {
final String returnTypeString = returnTypeMirror.toString();
w.append("new com.moparisthebest.jdbc.util.ResultSetToObject<")
.append(returnTypeString).append(">() {\n\t\t\t\t\tpublic ")
.append(returnTypeString).append(" toObject(final ResultSet rs, final java.util.Calendar ")
.append(cal == null ? "_cal" : cal)
.append(") throws SQLException {\n");
}
// com.moparisthebest.jdbc.util.ResultSetToObject implementation
writeObject(w, keys, returnTypeMirror, cal);
w.write("\t\t\t\t\t\treturn ");
clean(w, cleaner).write(";\n");
// end ResultSetToObject implementation
if(!java8)
w.write("\t\t\t\t\t}\n");
w.append("\t\t\t\t\t}\n\t\t\t\t: null, ").append(cal == null ? "null" : cal).append(")");
if(closePs)
w.append(".setPreparedStatementToClose(ps)");
w.append(";\n");
}
public void writeCollection(final Writer w, final String[] keys, final String returnTypeString, final String concreteTypeString, final TypeMirror componentTypeMirror, MaxRows maxRows, String cal, final String cleaner) throws IOException, ClassNotFoundException {
maxRowInit(w, maxRows).write("\t\t\tfinal ");
w.write(returnTypeString);

View File

@ -349,6 +349,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
for (final VariableElement param : bindParams)
setObject(w, ++count, databaseType, arrayNumberTypeName, arrayStringTypeName, param);
boolean closeRs = true;
if (!parsedSQl.isSelect()) {
if (returnType.equals("void")) {
w.write("\t\t\tps.executeUpdate();\n");
@ -370,20 +371,32 @@ public class JdbcMapperProcessor extends AbstractProcessor {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@JdbcMapper.SQL sql parsed a wildcard column name which is not supported", methodElement);
return false;
}
rsm.mapToResultType(w, keys, eeMethod, maxRows, calendarName, cleanerName);
}
// if no SQLException is thrown, we have to catch it here and wrap it with RuntimeException...
if (!sqlExceptionThrown) {
w.write("\t\t} catch(SQLException e) {\n\t\t\tthrow new RuntimeException(e);\n");
closeRs = rsm.mapToResultType(w, keys, eeMethod, maxRows, calendarName, cleanerName, !cachePreparedStatements);
}
// close things
w.write("\t\t} finally {\n");
if (parsedSQl.isSelect())
w.write("\t\t\ttryClose(rs);\n");
if (!cachePreparedStatements)
w.write("\t\t\ttryClose(ps);\n");
if(closeRs) {
// like normal
// if no SQLException is thrown, we have to catch it here and wrap it with RuntimeException...
if (!sqlExceptionThrown)
w.write("\t\t} catch(SQLException e) {\n\t\t\tthrow new RuntimeException(e);\n");
w.write("\t\t} finally {\n");
if (parsedSQl.isSelect())
w.write("\t\t\ttryClose(rs);\n");
if (!cachePreparedStatements)
w.write("\t\t\ttryClose(ps);\n");
} else {
// very annoying special handling in that if any exceptions are thrown, we have to close everything even if closeRs == false...
w.write("\t\t} catch(Throwable e) {\n");
if (parsedSQl.isSelect())
w.write("\t\t\ttryClose(rs);\n");
if (!cachePreparedStatements)
w.write("\t\t\ttryClose(ps);\n");
if (sqlExceptionThrown)
w.write("\t\t\tif(e instanceof SQLException)\n\t\t\t\tthrow (SQLException)e;\n");
w.write("\t\t\tif(e instanceof RuntimeException)\n\t\t\t\tthrow (RuntimeException)e;\n");
w.write("\t\t\tthrow new RuntimeException(e);\n");
}
w.write("\t\t}\n");
w.write("\t}\n");

View File

@ -1,12 +1,20 @@
package com.moparisthebest.jdbc.codegen;
import com.moparisthebest.jdbc.dto.FieldPerson;
import com.moparisthebest.jdbc.util.ResultSetIterable;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import static com.moparisthebest.jdbc.QueryMapperTest.fieldPerson1;
import static com.moparisthebest.jdbc.QueryMapperTest.getConnection;
import static com.moparisthebest.jdbc.QueryMapperTest.people;
import static com.moparisthebest.jdbc.TryClose.tryClose;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
/**
@ -24,7 +32,7 @@ public class JdbcMapperTest {
@AfterClass
public static void tearDown() throws Throwable {
//tryClose(dao);
tryClose(dao);
}
public PersonDAO getDao() {
@ -35,4 +43,30 @@ public class JdbcMapperTest {
public void testName() throws Throwable {
assertEquals(fieldPerson1.getFirstName(), getDao().getFirstName(fieldPerson1.getPersonNo()));
}
@Test
public void testList() throws SQLException {
final List<FieldPerson> fromDb = dao.getPeopleList(people[0].getPersonNo(), people[1].getPersonNo(), people[2].getPersonNo());
assertArrayEquals(people, fromDb.toArray());
}
@Test
public void testResultSetIterable() throws SQLException {
final ResultSetIterable<FieldPerson> rsi = dao.getPeopleResultSetIterable(people[0].getPersonNo(), people[1].getPersonNo(), people[2].getPersonNo());
final List<FieldPerson> fromDb = new ArrayList<FieldPerson>();
for(final FieldPerson fieldPerson : rsi)
fromDb.add(fieldPerson);
rsi.close();
assertArrayEquals(people, fromDb.toArray());
}
@Test
public void testResultSetIterableCachedPreparedStatement() throws SQLException {
final ResultSetIterable<FieldPerson> rsi = dao.getPeopleResultSetIterableCachedPreparedStatement(people[0].getPersonNo(), people[1].getPersonNo(), people[2].getPersonNo());
final List<FieldPerson> fromDb = new ArrayList<FieldPerson>();
for(final FieldPerson fieldPerson : rsi)
fromDb.add(fieldPerson);
rsi.close();
assertArrayEquals(people, fromDb.toArray());
}
}

View File

@ -3,7 +3,9 @@ package com.moparisthebest.jdbc.codegen;
import com.moparisthebest.jdbc.Cleaner;
import com.moparisthebest.jdbc.dto.FieldPerson;
import com.moparisthebest.jdbc.dto.Person;
import com.moparisthebest.jdbc.util.ResultSetIterable;
import java.io.Closeable;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
@ -15,10 +17,10 @@ import java.util.*;
@JdbcMapper.Mapper(
// jndiName = "bob",
// databaseType = JdbcMapper.DatabaseType.ORACLE
// cachePreparedStatements = false
cachePreparedStatements = JdbcMapper.OptionalBool.FALSE
// , sqlParser = SimpleSQLParser.class
)
public interface PersonDAO {
public interface PersonDAO extends Closeable {
@JdbcMapper.SQL("UPDATE person SET first_name = {firstName} WHERE last_name = {lastName}")
int setFirstName(String firstName, String lastName);
@ -132,4 +134,14 @@ public interface PersonDAO {
Map<String, FieldPerson> getPersonStaticLimitMap(long personNo) throws SQLException;
@JdbcMapper.SQL(value = "SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}", maxRows = Long.MAX_VALUE)
Map<String, List<FieldPerson>> getPersonStaticLimitMapList(long personNo) throws SQLException;
@JdbcMapper.SQL("SELECT person_no, birth_date, last_name, first_name from person WHERE person_no IN ({personNo1},{personNo2},{personNo3}) ORDER BY person_no")
List<FieldPerson> getPeopleList(long personNo1, long personNo2, long personNo3) throws SQLException;
@JdbcMapper.SQL("SELECT person_no, birth_date, last_name, first_name from person WHERE person_no IN ({personNo1},{personNo2},{personNo3}) ORDER BY person_no")
ResultSetIterable<FieldPerson> getPeopleResultSetIterable(long personNo1, long personNo2, long personNo3) throws SQLException;
@JdbcMapper.SQL(value = "SELECT person_no, birth_date, last_name, first_name from person WHERE person_no IN ({personNo1},{personNo2},{personNo3}) ORDER BY person_no", cachePreparedStatement = JdbcMapper.OptionalBool.TRUE)
ResultSetIterable<FieldPerson> getPeopleResultSetIterableCachedPreparedStatement(long personNo1, long personNo2, long personNo3) throws SQLException;
}