mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-11-24 10:02:14 -05:00
Remove requirement for @JdbcMapper.Mapper to implement JdbcMapper, logic around requiring close() method or not
This commit is contained in:
parent
5960341297
commit
2f19f2ad46
@ -25,8 +25,15 @@ public interface JdbcMapper extends Closeable {
|
|||||||
*/
|
*/
|
||||||
String jndiName() default "";
|
String jndiName() default "";
|
||||||
|
|
||||||
boolean cachePreparedStatements() default true;
|
/**
|
||||||
|
* This defaults to true if a close() method exists to override/implement, false otherwise
|
||||||
|
*/
|
||||||
|
OptionalBool cachePreparedStatements() default OptionalBool.DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This defaults to SimpleSQLParser, PrestoSQLParser is another option for Java 8, or implement your own
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
Class<? extends SQLParser> sqlParser() default SQLParser.class;
|
Class<? extends SQLParser> sqlParser() default SQLParser.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +45,10 @@ public interface JdbcMapper extends Closeable {
|
|||||||
*/
|
*/
|
||||||
String value();
|
String value();
|
||||||
|
|
||||||
OptionalBool cachePreparedStatement() default OptionalBool.INHERIT;
|
/**
|
||||||
|
* This defaults to the value of the class-level @JdbcMapper.Mapper.cachePreparedStatements annotation, but can be configured on a per-method level here
|
||||||
|
*/
|
||||||
|
OptionalBool cachePreparedStatement() default OptionalBool.DEFAULT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum array length.
|
* Maximum array length.
|
||||||
@ -56,18 +66,18 @@ public interface JdbcMapper extends Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum OptionalBool {
|
public enum OptionalBool {
|
||||||
INHERIT,
|
DEFAULT,
|
||||||
TRUE,
|
TRUE,
|
||||||
FALSE;
|
FALSE;
|
||||||
|
|
||||||
public boolean combine(final boolean inherited) {
|
public boolean combine(final boolean def) {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case TRUE:
|
case TRUE:
|
||||||
return true;
|
return true;
|
||||||
case FALSE:
|
case FALSE:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return inherited;
|
return def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ public abstract class JdbcMapperFactory {
|
|||||||
static final String SUFFIX = "Bean";
|
static final String SUFFIX = "Bean";
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T extends JdbcMapper> T create(final Class<T> jdbcMapper, final Connection connection) {
|
public static <T> T create(final Class<T> jdbcMapper, final Connection connection) {
|
||||||
try {
|
try {
|
||||||
return (T) Class.forName(jdbcMapper.getName() + SUFFIX).getConstructor(Connection.class).newInstance(connection);
|
return (T) Class.forName(jdbcMapper.getName() + SUFFIX).getConstructor(Connection.class).newInstance(connection);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@ -18,7 +18,7 @@ public abstract class JdbcMapperFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends JdbcMapper> T create(final Class<T> jdbcMapper) {
|
public static <T> T create(final Class<T> jdbcMapper) {
|
||||||
return create(jdbcMapper, null);
|
return create(jdbcMapper, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,17 +146,36 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
w.write("\n\t\tif (this.conn == null)\n" +
|
w.write("\n\t\tif (this.conn == null)\n" +
|
||||||
"\t\t\tthrow new NullPointerException(\"Connection needs to be non-null for JdbcMapper...\");\n\t}\n" +
|
"\t\t\tthrow new NullPointerException(\"Connection needs to be non-null for JdbcMapper...\");\n\t}\n" +
|
||||||
"\n\t@Override\n\tpublic Connection getConnection() {\n\t\treturn this.conn;\n\t}\n"
|
"\n\tpublic Connection getConnection() {\n\t\treturn this.conn;\n\t}\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
// loop through methods
|
// loop through methods
|
||||||
final Types typeUtils = processingEnv.getTypeUtils();
|
|
||||||
int cachedPreparedStatements = 0;
|
int cachedPreparedStatements = 0;
|
||||||
|
ExecutableElement closeMethod = null;
|
||||||
|
boolean lookupCloseMethod = true;
|
||||||
|
final boolean defaultCachePreparedStatements;
|
||||||
|
switch (mapper.cachePreparedStatements()) {
|
||||||
|
case TRUE:
|
||||||
|
defaultCachePreparedStatements = true;
|
||||||
|
break;
|
||||||
|
case FALSE:
|
||||||
|
defaultCachePreparedStatements = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
defaultCachePreparedStatements = (closeMethod = getCloseMethod(genClass)) != null;
|
||||||
|
lookupCloseMethod = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (final Element methodElement : genClass.getEnclosedElements()) {
|
for (final Element methodElement : genClass.getEnclosedElements()) {
|
||||||
// can only implement abstract methods
|
// can only implement abstract methods
|
||||||
if (methodElement.getKind() != ElementKind.METHOD || !methodElement.getModifiers().contains(Modifier.ABSTRACT))
|
if (methodElement.getKind() != ElementKind.METHOD || !methodElement.getModifiers().contains(Modifier.ABSTRACT))
|
||||||
continue;
|
continue;
|
||||||
final ExecutableElement eeMethod = (ExecutableElement) methodElement;
|
final ExecutableElement eeMethod = (ExecutableElement) methodElement;
|
||||||
|
if(lookupCloseMethod)
|
||||||
|
if((closeMethod = getCloseMethod(eeMethod)) != null) {
|
||||||
|
lookupCloseMethod = false;
|
||||||
|
continue; // skip close method
|
||||||
|
}
|
||||||
final JdbcMapper.SQL sql = eeMethod.getAnnotation(JdbcMapper.SQL.class);
|
final JdbcMapper.SQL sql = eeMethod.getAnnotation(JdbcMapper.SQL.class);
|
||||||
if (sql == null || sql.value().isEmpty()) {
|
if (sql == null || sql.value().isEmpty()) {
|
||||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@JdbcMapper.SQL with non-empty query is required on abstract or interface methods", methodElement);
|
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@JdbcMapper.SQL with non-empty query is required on abstract or interface methods", methodElement);
|
||||||
@ -199,7 +218,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
w.write(" throws ");
|
w.write(" throws ");
|
||||||
}
|
}
|
||||||
for (final TypeMirror thrownType : thrownTypes) {
|
for (final TypeMirror thrownType : thrownTypes) {
|
||||||
sqlExceptionThrown |= typeUtils.isSameType(thrownType, sqlExceptionType);
|
sqlExceptionThrown |= types.isSameType(thrownType, sqlExceptionType);
|
||||||
w.write(thrownType.toString());
|
w.write(thrownType.toString());
|
||||||
if (++count != numThrownTypes)
|
if (++count != numThrownTypes)
|
||||||
w.write(", ");
|
w.write(", ");
|
||||||
@ -228,7 +247,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
if (parsedSQl.isSelect())
|
if (parsedSQl.isSelect())
|
||||||
w.write("\t\tResultSet rs = null;\n");
|
w.write("\t\tResultSet rs = null;\n");
|
||||||
w.write("\t\ttry {\n\t\t\tps = ");
|
w.write("\t\ttry {\n\t\t\tps = ");
|
||||||
final boolean cachePreparedStatements = sql.cachePreparedStatement().combine(mapper.cachePreparedStatements());
|
final boolean cachePreparedStatements = sql.cachePreparedStatement().combine(defaultCachePreparedStatements);
|
||||||
if (cachePreparedStatements) {
|
if (cachePreparedStatements) {
|
||||||
w.write("this.prepareStatement(");
|
w.write("this.prepareStatement(");
|
||||||
w.write(Integer.toString(cachedPreparedStatements));
|
w.write(Integer.toString(cachedPreparedStatements));
|
||||||
@ -286,6 +305,15 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
w.write("\t}\n");
|
w.write("\t}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// look on super classes and interfaces recursively
|
||||||
|
if(lookupCloseMethod)
|
||||||
|
closeMethod = getCloseMethod(genClass);
|
||||||
|
|
||||||
|
if(closeMethod == null && (cachedPreparedStatements > 0 || doJndi)) {
|
||||||
|
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@Jdbc.Mapper extended classes with cachedPreparedStatements or jndiNames must have a public void close() method to override or implement, because they must be closed", genClass);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (cachedPreparedStatements > 0) {
|
if (cachedPreparedStatements > 0) {
|
||||||
w.write("\n\tprivate final PreparedStatement[] psCache = new PreparedStatement[");
|
w.write("\n\tprivate final PreparedStatement[] psCache = new PreparedStatement[");
|
||||||
w.write(Integer.toString(cachedPreparedStatements));
|
w.write(Integer.toString(cachedPreparedStatements));
|
||||||
@ -296,13 +324,18 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// close method
|
// close method
|
||||||
w.write("\n\t@Override\n\tpublic void close() {\n\t\t");
|
if(closeMethod != null) {
|
||||||
if (cachedPreparedStatements > 0)
|
// if cachedPreparedStatements > 0 or doJndi are true, class MUST have a close() method to override as it
|
||||||
w.write("for(final PreparedStatement ps : psCache)\n\t\t\ttryClose(ps);\n\t\t");
|
// MUST be called to clean up
|
||||||
w.write("tryClose(conn);\n");
|
w.write("\n\t@Override\n\tpublic void close() {\n");
|
||||||
if (doJndi)
|
if (cachedPreparedStatements > 0)
|
||||||
w.write("\t\ttryClose(ctx);\n");
|
w.write("\t\tfor(final PreparedStatement ps : psCache)\n\t\t\ttryClose(ps);\n");
|
||||||
w.write("\t}\n");
|
if (doJndi)
|
||||||
|
w.write("\t\ttryClose(ctx);\n\t\tif(ctx != null)\n\t\t\ttryClose(conn);\n");
|
||||||
|
if(closeMethod.getEnclosingElement().getKind() != ElementKind.INTERFACE && !closeMethod.getEnclosingElement().equals(genClass))
|
||||||
|
w.write("\t\tsuper.close();\n");
|
||||||
|
w.write("\t}\n");
|
||||||
|
}
|
||||||
// end close method
|
// end close method
|
||||||
|
|
||||||
w.write("}\n");
|
w.write("}\n");
|
||||||
@ -408,4 +441,32 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
return Class.forName(tm.toString());
|
return Class.forName(tm.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExecutableElement getCloseMethod(final TypeElement genClass) {
|
||||||
|
ExecutableElement ret = null;
|
||||||
|
for (final Element methodElement : genClass.getEnclosedElements()) {
|
||||||
|
if((ret = getCloseMethod(methodElement)) != null)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// superclasses
|
||||||
|
final TypeMirror superclass = genClass.getSuperclass();
|
||||||
|
if(superclass.getKind() == TypeKind.DECLARED && (ret = getCloseMethod((TypeElement)types.asElement(superclass))) != null)
|
||||||
|
return ret;
|
||||||
|
// interfaces
|
||||||
|
for(final TypeMirror iface : genClass.getInterfaces()) {
|
||||||
|
if(iface.getKind() == TypeKind.DECLARED && (ret = getCloseMethod((TypeElement)types.asElement(iface))) != null)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExecutableElement getCloseMethod(final Element element) {
|
||||||
|
return element.getKind() != ElementKind.METHOD ? null : getCloseMethod((ExecutableElement) element);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExecutableElement getCloseMethod(final ExecutableElement methodElement) {
|
||||||
|
return methodElement.getReturnType().getKind() == TypeKind.VOID &&
|
||||||
|
methodElement.getSimpleName().toString().equals("close") &&
|
||||||
|
methodElement.getParameters().isEmpty() ? methodElement : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ public class JdbcMapperTest {
|
|||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void tearDown() throws Throwable {
|
public static void tearDown() throws Throwable {
|
||||||
tryClose(dao);
|
//tryClose(dao);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PersonDAO getDao() {
|
public PersonDAO getDao() {
|
||||||
|
@ -2,6 +2,7 @@ package com.moparisthebest.jdbc.codegen;
|
|||||||
|
|
||||||
import com.moparisthebest.jdbc.dto.FieldPerson;
|
import com.moparisthebest.jdbc.dto.FieldPerson;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -13,10 +14,10 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@JdbcMapper.Mapper(
|
@JdbcMapper.Mapper(
|
||||||
// jndiName = "bob",
|
// jndiName = "bob",
|
||||||
cachePreparedStatements = false
|
// cachePreparedStatements = false
|
||||||
// , sqlParser = SimpleSQLParser.class
|
// , sqlParser = SimpleSQLParser.class
|
||||||
)
|
)
|
||||||
public interface PersonDAO extends JdbcMapper {
|
public interface PersonDAO {
|
||||||
|
|
||||||
@JdbcMapper.SQL("UPDATE person SET first_name = {firstName} WHERE last_name = {lastName}")
|
@JdbcMapper.SQL("UPDATE person SET first_name = {firstName} WHERE last_name = {lastName}")
|
||||||
int setFirstName(String firstName, String lastName);
|
int setFirstName(String firstName, String lastName);
|
||||||
|
@ -13,8 +13,8 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@JdbcMapper.Mapper(
|
@JdbcMapper.Mapper(
|
||||||
// jndiName = "bob",
|
// jndiName = "bob",
|
||||||
cachePreparedStatements = false
|
// cachePreparedStatements = false,
|
||||||
, sqlParser = PrestoSQLParser.class
|
sqlParser = PrestoSQLParser.class
|
||||||
)
|
)
|
||||||
public interface PrestoPersonDAO extends PersonDAO {
|
public interface PrestoPersonDAO extends PersonDAO {
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ public class PrestoPersonDAOTest extends JdbcMapperTest {
|
|||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void tearDown() throws Throwable {
|
public static void tearDown() throws Throwable {
|
||||||
tryClose(dao);
|
//tryClose(dao);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PersonDAO getDao() {
|
public PersonDAO getDao() {
|
||||||
|
Loading…
Reference in New Issue
Block a user