mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-12-21 23:08:52 -05:00
Tweak JdbcMapperFactory, now used for QueryMapper and JdbcMapper generated classes
This commit is contained in:
parent
08e2c9354d
commit
e246af935f
@ -8,6 +8,8 @@ import javax.naming.NamingException;
|
||||
import javax.sql.DataSource;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@ -20,9 +22,23 @@ public class JdbcMapperFactory<T> implements Factory<T> {
|
||||
|
||||
static final String SUFFIX = "Bean";
|
||||
|
||||
static {
|
||||
try{
|
||||
final Class<?> ensureContext = Class.forName(System.getProperty("QueryMapper.ensureContext.class", System.getProperty("JdbcMapper.ensureContext.class", "com.gcl.containerless.EnsureContext")));
|
||||
final Method method = ensureContext.getMethod(System.getProperty("QueryMapper.ensureContext.method", System.getProperty("JdbcMapper.ensureContext.method", "setup")));
|
||||
method.invoke(null);
|
||||
}catch(Throwable e){
|
||||
// ignore
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> getImplementationClass(final Class<T> jdbcMapper) throws ClassNotFoundException {
|
||||
return (Class<? extends T>) Class.forName(jdbcMapper.getName() + SUFFIX);
|
||||
if(jdbcMapper.isInterface() || Modifier.isAbstract(jdbcMapper.getModifiers()))
|
||||
return (Class<? extends T>) Class.forName(jdbcMapper.getName() + SUFFIX);
|
||||
else
|
||||
return jdbcMapper;
|
||||
}
|
||||
|
||||
public static <T> Constructor<? extends T> getConnectionConstructor(final Class<T> jdbcMapper) throws ClassNotFoundException, NoSuchMethodException {
|
||||
@ -45,6 +61,14 @@ public class JdbcMapperFactory<T> implements Factory<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T create(final Class<T> jdbcMapper, final Factory<Connection> connectionFactory) {
|
||||
try {
|
||||
return getFactoryConstructor(jdbcMapper).newInstance(connectionFactory);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("could not create JdbcMapper, did the processor run at compile time?", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T create(final Class<T> jdbcMapper) {
|
||||
try {
|
||||
return getDefaultConstructor(jdbcMapper).newInstance();
|
||||
@ -115,10 +139,22 @@ public class JdbcMapperFactory<T> implements Factory<T> {
|
||||
;
|
||||
}
|
||||
|
||||
public static <T> Factory<T> of(final Class<T> jdbcMapper) {
|
||||
return new JdbcMapperFactory<T>(jdbcMapper);
|
||||
}
|
||||
|
||||
public static <T> Factory<T> of(final Class<T> jdbcMapper, final Factory<Connection> connectionFactory) {
|
||||
return new JdbcMapperFactory<T>(jdbcMapper, connectionFactory);
|
||||
}
|
||||
|
||||
public static <T> Factory<T> of(final Class<T> jdbcMapper, final String jndiName) {
|
||||
return of(jdbcMapper, connectionFactory(jndiName));
|
||||
}
|
||||
|
||||
private final Constructor<? extends T> constructor;
|
||||
private final Object[] args;
|
||||
|
||||
public JdbcMapperFactory(final Class<T> jdbcMapper) {
|
||||
private JdbcMapperFactory(final Class<T> jdbcMapper) {
|
||||
try {
|
||||
this.constructor = getDefaultConstructor(jdbcMapper);
|
||||
this.args = null;
|
||||
@ -127,39 +163,19 @@ public class JdbcMapperFactory<T> implements Factory<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public JdbcMapperFactory(final Constructor<? extends T> constructor, final Object... args) {
|
||||
if (constructor == null)
|
||||
throw new NullPointerException("constructor must be non-null");
|
||||
this.constructor = constructor;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public JdbcMapperFactory(final Class<T> queryMapperClass, final Factory<Connection> connectionFactory) {
|
||||
if (queryMapperClass == null)
|
||||
throw new NullPointerException("queryMapperClass must be non-null");
|
||||
private JdbcMapperFactory(final Class<T> jdbcMapper, final Factory<Connection> connectionFactory) {
|
||||
if (jdbcMapper == null)
|
||||
throw new NullPointerException("jdbcMapper must be non-null");
|
||||
if (connectionFactory == null)
|
||||
throw new NullPointerException("connectionFactory must be non-null");
|
||||
try {
|
||||
this.constructor = queryMapperClass.getConstructor(Factory.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("queryMapperClass must have a constructor that takes Factory<Connection>", e);
|
||||
this.constructor = getFactoryConstructor(jdbcMapper);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("jdbcMapper must have a constructor that takes Factory<Connection>", e);
|
||||
}
|
||||
this.args = new Object[]{connectionFactory};
|
||||
}
|
||||
|
||||
public JdbcMapperFactory(final Class<T> queryMapperClass, final String jndiName) {
|
||||
if (queryMapperClass == null)
|
||||
throw new NullPointerException("queryMapperClass must be non-null");
|
||||
if (jndiName == null)
|
||||
throw new NullPointerException("jndiName must be non-null");
|
||||
try {
|
||||
this.constructor = queryMapperClass.getConstructor(String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("queryMapperClass must have a constructor that takes String", e);
|
||||
}
|
||||
this.args = new Object[]{jndiName};
|
||||
}
|
||||
|
||||
@Override
|
||||
public T create() throws SQLException {
|
||||
try {
|
||||
|
@ -149,10 +149,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
w.write(packageName);
|
||||
w.write(";\n\n");
|
||||
}
|
||||
if (doJndi) {
|
||||
w.write("import javax.naming.InitialContext;\n");
|
||||
w.write("import javax.sql.DataSource;\n");
|
||||
}
|
||||
w.write("import com.moparisthebest.jdbc.Factory;\n\n");
|
||||
w.write("import java.sql.*;\n\n");
|
||||
w.write("import static com.moparisthebest.jdbc.util.ResultSetUtil.*;\n");
|
||||
w.write("import static com.moparisthebest.jdbc.TryClose.tryClose;\n\n");
|
||||
@ -164,36 +161,16 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
w.write(" extends ");
|
||||
}
|
||||
w.write(genClass.getSimpleName().toString());
|
||||
w.write(" {\n\n\tprivate final Connection conn;\n");
|
||||
w.write(" {\n\n\tprivate final Connection conn;\n\tprivate final boolean closeConn;\n\n");
|
||||
if (doJndi) {
|
||||
w.write("\tprivate final InitialContext ctx;\n\n\tpublic ");
|
||||
w.write("\tprivate static final Factory<Connection> _conFactory = com.moparisthebest.jdbc.codegen.JdbcMapperFactory.connectionFactory(\"");
|
||||
w.append(mapper.jndiName()).append("\");\n\n\tpublic ");
|
||||
w.write(className);
|
||||
w.write("() {\n\t\tthis(null);\n\t}\n");
|
||||
w.write("() throws SQLException {\n\t\tthis(_conFactory);\n\t}\n");
|
||||
}
|
||||
w.write("\n\tpublic ");
|
||||
w.write(className);
|
||||
w.write("(Connection conn) {\n\t\t");
|
||||
if (doJndi) {
|
||||
w.write("InitialContext ctx = null;\n" +
|
||||
"\t\tif (conn == null)\n" +
|
||||
"\t\t\ttry {\n" +
|
||||
"\t\t\t\tctx = new InitialContext();\n" +
|
||||
"\t\t\t\tDataSource ds = (DataSource) ctx.lookup(\"");
|
||||
w.write(mapper.jndiName()); // todo: escape this? I don't think anyone needs that, for now...
|
||||
w.write("\");\n" +
|
||||
"\t\t\t\tconn = ds.getConnection();\n" +
|
||||
"\t\t\t} catch (Throwable e) {\n" +
|
||||
"\t\t\t\ttryClose(ctx);\n" +
|
||||
"\t\t\t\ttryClose(conn);\n" +
|
||||
"\t\t\t\tthrow new RuntimeException(e);\n" +
|
||||
"\t\t\t}\n" +
|
||||
"\t\tthis.conn = conn;\n" +
|
||||
"\t\tthis.ctx = ctx;"
|
||||
);
|
||||
} else {
|
||||
w.write("this.conn = conn;");
|
||||
}
|
||||
w.write("\n\t\tif (this.conn == null)\n" +
|
||||
w.write("(Connection conn) {\n\t\tthis.conn = conn;\n\t\tthis.closeConn = false;\n\t\tif (this.conn == null)\n" +
|
||||
"\t\t\tthrow new NullPointerException(\"Connection needs to be non-null for JdbcMapper...\");\n\t}\n" +
|
||||
"\n\tpublic Connection getConnection() {\n\t\treturn this.conn;\n\t}\n"
|
||||
);
|
||||
@ -458,10 +435,22 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
if (cachedPreparedStatements > 0)
|
||||
w.write("\t\tfor(final PreparedStatement ps : psCache)\n\t\t\ttryClose(ps);\n");
|
||||
if (doJndi)
|
||||
w.write("\t\ttryClose(ctx);\n\t\tif(ctx != null)\n\t\t\ttryClose(conn);\n");
|
||||
w.write("\t\tif(closeConn)\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");
|
||||
|
||||
// and we can create constructors that set closeConn to true!
|
||||
w.write("\n\tpublic ");
|
||||
w.write(className);
|
||||
w.write("(final Factory<Connection> connectionFactory) throws SQLException {\n\t\tthis.conn = connectionFactory.create();\n\t\tthis.closeConn = true;\n\t\tif (this.conn == null)\n" +
|
||||
"\t\t\tthrow new NullPointerException(\"Connection needs to be non-null for JdbcMapper...\");\n\t}\n"
|
||||
);
|
||||
|
||||
w.write("\n\tpublic ");
|
||||
w.write(className);
|
||||
w.write("(final String jndiName) throws SQLException {\n\t\tthis(com.moparisthebest.jdbc.codegen.JdbcMapperFactory.connectionFactory(jndiName));\n\t}\n"
|
||||
);
|
||||
}
|
||||
// end close method
|
||||
|
||||
|
@ -28,7 +28,7 @@ import java.time.*;
|
||||
// , sqlParser = SimpleSQLParser.class
|
||||
, allowReflection = JdbcMapper.OptionalBool.TRUE
|
||||
)
|
||||
public interface PersonDAO extends Closeable {
|
||||
public interface PersonDAO extends JdbcMapper {
|
||||
|
||||
@JdbcMapper.SQL("CREATE TABLE person (person_no NUMERIC, first_name VARCHAR(40), last_name VARCHAR(40), birth_date TIMESTAMP)")
|
||||
void createTablePerson();
|
||||
|
@ -0,0 +1,39 @@
|
||||
package com.moparisthebest.jdbc.codegen;
|
||||
|
||||
import com.moparisthebest.jdbc.*;
|
||||
import com.moparisthebest.jdbc.dto.Person;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static com.moparisthebest.jdbc.QueryMapperTest.fieldPerson1;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Created by mopar on 7/3/17.
|
||||
*/
|
||||
public class PersonDAOQueryRunnerTest {
|
||||
|
||||
public static final QueryRunner<PersonDAO> pqr = QueryRunner.withRetry(JdbcMapperFactory.of(PersonDAO.class, new Factory<Connection>() {
|
||||
@Override
|
||||
public Connection create() throws SQLException {
|
||||
return QueryMapperTest.getConnection();
|
||||
}
|
||||
}), 10, QueryRunner.fixedDelay(5).withJitter(5));
|
||||
|
||||
@Test
|
||||
public void testPerson() throws Exception {
|
||||
//final QueryRunner<ListQueryMapper> lqr = pqr.withFactory(() -> new ListQueryMapper(QueryMapperTest::getConnection));
|
||||
assertEquals(fieldPerson1, pqr.runRetryFuture(new QueryRunner.Runner<PersonDAO, Person>() {
|
||||
@Override
|
||||
public Person run(final PersonDAO dao) throws SQLException {
|
||||
if(Math.random() < 0.5) {
|
||||
System.out.println("fake fail");
|
||||
throw new SQLException("fake 50% failure rate");
|
||||
}
|
||||
return dao.getPerson(fieldPerson1.getPersonNo());
|
||||
}
|
||||
}).get());
|
||||
}
|
||||
}
|
@ -1,13 +1,10 @@
|
||||
package com.moparisthebest.jdbc;
|
||||
|
||||
import com.moparisthebest.jdbc.codegen.JdbcMapper;
|
||||
import com.moparisthebest.jdbc.codegen.JdbcMapperFactory;
|
||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.sql.DataSource;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
//IFJAVA8_START
|
||||
@ -22,27 +19,16 @@ public class QueryMapper implements JdbcMapper {
|
||||
public static final Object noBind = new Object();
|
||||
public static final ResultSetMapper defaultRsm = new ResultSetMapper();
|
||||
|
||||
static {
|
||||
try{
|
||||
final Class<?> ensureContext = Class.forName(System.getProperty("QueryMapper.ensureContext.class", "com.gcl.containerless.EnsureContext"));
|
||||
final Method method = ensureContext.getMethod(System.getProperty("QueryMapper.ensureContext.method", "setup"));
|
||||
method.invoke(null);
|
||||
}catch(Throwable e){
|
||||
// ignore
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected final ResultSetMapper cm;
|
||||
protected final Connection conn;
|
||||
protected final Context context;
|
||||
protected final boolean closeConn;
|
||||
|
||||
protected QueryMapper(Connection conn, final String jndiName, final Factory<Connection> factory, final ResultSetMapper cm) {
|
||||
protected QueryMapper(Connection conn, final String jndiName, Factory<Connection> factory, final ResultSetMapper cm) {
|
||||
this.cm = cm == null ? defaultRsm : cm;
|
||||
boolean closeConn = false;
|
||||
Context context = null;
|
||||
if(conn == null) {
|
||||
if(factory == null && jndiName != null)
|
||||
factory = JdbcMapperFactory.connectionFactory(jndiName);
|
||||
if (factory != null) {
|
||||
try {
|
||||
conn = factory.create();
|
||||
@ -50,21 +36,11 @@ public class QueryMapper implements JdbcMapper {
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("factory failed to create connection", e);
|
||||
}
|
||||
} else if (jndiName != null)
|
||||
try {
|
||||
context = new InitialContext();
|
||||
DataSource ds = (DataSource) context.lookup(jndiName);
|
||||
conn = ds.getConnection();
|
||||
closeConn = true;
|
||||
} catch (Throwable e) {
|
||||
tryClose(context);
|
||||
throw new RuntimeException("JNDI lookup failed to create connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (conn == null)
|
||||
throw new NullPointerException("Connection needs to be non-null for QueryMapper...");
|
||||
this.conn = conn;
|
||||
this.context = context;
|
||||
this.closeConn = closeConn;
|
||||
}
|
||||
|
||||
@ -98,7 +74,6 @@ public class QueryMapper implements JdbcMapper {
|
||||
protected QueryMapper() {
|
||||
this.cm = null;
|
||||
this.conn = null;
|
||||
this.context = null;
|
||||
this.closeConn = false;
|
||||
}
|
||||
|
||||
@ -106,7 +81,6 @@ public class QueryMapper implements JdbcMapper {
|
||||
public void close() {
|
||||
if (closeConn) {
|
||||
tryClose(conn);
|
||||
tryClose(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ import static com.moparisthebest.jdbc.QueryMapperTest.*;
|
||||
* Created by mopar on 7/1/17.
|
||||
*/
|
||||
public class QueryRunnerTest {
|
||||
public static final QueryRunner<QueryMapper> qr = QueryRunner.withRetry(new JdbcMapperFactory<QueryMapper>(QueryMapper.class, new Factory<Connection>() {
|
||||
public static final QueryRunner<QueryMapper> qr = QueryRunner.withRetry(JdbcMapperFactory.of(QueryMapper.class, new Factory<Connection>() {
|
||||
@Override
|
||||
public Connection create() throws SQLException {
|
||||
return QueryMapperTest.getConnection();
|
||||
|
Loading…
Reference in New Issue
Block a user