mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-11-22 00:52:16 -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 javax.sql.DataSource;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@ -20,9 +22,23 @@ public class JdbcMapperFactory<T> implements Factory<T> {
|
|||||||
|
|
||||||
static final String SUFFIX = "Bean";
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> Class<? extends T> getImplementationClass(final Class<T> jdbcMapper) throws ClassNotFoundException {
|
public static <T> Class<? extends T> getImplementationClass(final Class<T> jdbcMapper) throws ClassNotFoundException {
|
||||||
|
if(jdbcMapper.isInterface() || Modifier.isAbstract(jdbcMapper.getModifiers()))
|
||||||
return (Class<? extends T>) Class.forName(jdbcMapper.getName() + SUFFIX);
|
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 {
|
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) {
|
public static <T> T create(final Class<T> jdbcMapper) {
|
||||||
try {
|
try {
|
||||||
return getDefaultConstructor(jdbcMapper).newInstance();
|
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 Constructor<? extends T> constructor;
|
||||||
private final Object[] args;
|
private final Object[] args;
|
||||||
|
|
||||||
public JdbcMapperFactory(final Class<T> jdbcMapper) {
|
private JdbcMapperFactory(final Class<T> jdbcMapper) {
|
||||||
try {
|
try {
|
||||||
this.constructor = getDefaultConstructor(jdbcMapper);
|
this.constructor = getDefaultConstructor(jdbcMapper);
|
||||||
this.args = null;
|
this.args = null;
|
||||||
@ -127,39 +163,19 @@ public class JdbcMapperFactory<T> implements Factory<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public JdbcMapperFactory(final Constructor<? extends T> constructor, final Object... args) {
|
private JdbcMapperFactory(final Class<T> jdbcMapper, final Factory<Connection> connectionFactory) {
|
||||||
if (constructor == null)
|
if (jdbcMapper == null)
|
||||||
throw new NullPointerException("constructor must be non-null");
|
throw new NullPointerException("jdbcMapper 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");
|
|
||||||
if (connectionFactory == null)
|
if (connectionFactory == null)
|
||||||
throw new NullPointerException("connectionFactory must be non-null");
|
throw new NullPointerException("connectionFactory must be non-null");
|
||||||
try {
|
try {
|
||||||
this.constructor = queryMapperClass.getConstructor(Factory.class);
|
this.constructor = getFactoryConstructor(jdbcMapper);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (Throwable e) {
|
||||||
throw new RuntimeException("queryMapperClass must have a constructor that takes Factory<Connection>", e);
|
throw new RuntimeException("jdbcMapper must have a constructor that takes Factory<Connection>", e);
|
||||||
}
|
}
|
||||||
this.args = new Object[]{connectionFactory};
|
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
|
@Override
|
||||||
public T create() throws SQLException {
|
public T create() throws SQLException {
|
||||||
try {
|
try {
|
||||||
|
@ -149,10 +149,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
w.write(packageName);
|
w.write(packageName);
|
||||||
w.write(";\n\n");
|
w.write(";\n\n");
|
||||||
}
|
}
|
||||||
if (doJndi) {
|
w.write("import com.moparisthebest.jdbc.Factory;\n\n");
|
||||||
w.write("import javax.naming.InitialContext;\n");
|
|
||||||
w.write("import javax.sql.DataSource;\n");
|
|
||||||
}
|
|
||||||
w.write("import java.sql.*;\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.util.ResultSetUtil.*;\n");
|
||||||
w.write("import static com.moparisthebest.jdbc.TryClose.tryClose;\n\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(" extends ");
|
||||||
}
|
}
|
||||||
w.write(genClass.getSimpleName().toString());
|
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) {
|
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(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("\n\tpublic ");
|
||||||
w.write(className);
|
w.write(className);
|
||||||
w.write("(Connection conn) {\n\t\t");
|
w.write("(Connection conn) {\n\t\tthis.conn = conn;\n\t\tthis.closeConn = false;\n\t\tif (this.conn == null)\n" +
|
||||||
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" +
|
|
||||||
"\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\tpublic Connection getConnection() {\n\t\treturn this.conn;\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)
|
if (cachedPreparedStatements > 0)
|
||||||
w.write("\t\tfor(final PreparedStatement ps : psCache)\n\t\t\ttryClose(ps);\n");
|
w.write("\t\tfor(final PreparedStatement ps : psCache)\n\t\t\ttryClose(ps);\n");
|
||||||
if (doJndi)
|
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))
|
if (closeMethod.getEnclosingElement().getKind() != ElementKind.INTERFACE && !closeMethod.getEnclosingElement().equals(genClass))
|
||||||
w.write("\t\tsuper.close();\n");
|
w.write("\t\tsuper.close();\n");
|
||||||
w.write("\t}\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
|
// end close method
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import java.time.*;
|
|||||||
// , sqlParser = SimpleSQLParser.class
|
// , sqlParser = SimpleSQLParser.class
|
||||||
, allowReflection = JdbcMapper.OptionalBool.TRUE
|
, 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)")
|
@JdbcMapper.SQL("CREATE TABLE person (person_no NUMERIC, first_name VARCHAR(40), last_name VARCHAR(40), birth_date TIMESTAMP)")
|
||||||
void createTablePerson();
|
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;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
import com.moparisthebest.jdbc.codegen.JdbcMapper;
|
import com.moparisthebest.jdbc.codegen.JdbcMapper;
|
||||||
|
import com.moparisthebest.jdbc.codegen.JdbcMapperFactory;
|
||||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||||
|
|
||||||
import javax.naming.Context;
|
|
||||||
import javax.naming.InitialContext;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
//IFJAVA8_START
|
//IFJAVA8_START
|
||||||
@ -22,27 +19,16 @@ public class QueryMapper implements JdbcMapper {
|
|||||||
public static final Object noBind = new Object();
|
public static final Object noBind = new Object();
|
||||||
public static final ResultSetMapper defaultRsm = new ResultSetMapper();
|
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 ResultSetMapper cm;
|
||||||
protected final Connection conn;
|
protected final Connection conn;
|
||||||
protected final Context context;
|
|
||||||
protected final boolean closeConn;
|
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;
|
this.cm = cm == null ? defaultRsm : cm;
|
||||||
boolean closeConn = false;
|
boolean closeConn = false;
|
||||||
Context context = null;
|
|
||||||
if(conn == null) {
|
if(conn == null) {
|
||||||
|
if(factory == null && jndiName != null)
|
||||||
|
factory = JdbcMapperFactory.connectionFactory(jndiName);
|
||||||
if (factory != null) {
|
if (factory != null) {
|
||||||
try {
|
try {
|
||||||
conn = factory.create();
|
conn = factory.create();
|
||||||
@ -50,21 +36,11 @@ public class QueryMapper implements JdbcMapper {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException("factory failed to create connection", 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)
|
if (conn == null)
|
||||||
throw new NullPointerException("Connection needs to be non-null for QueryMapper...");
|
throw new NullPointerException("Connection needs to be non-null for QueryMapper...");
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.context = context;
|
|
||||||
this.closeConn = closeConn;
|
this.closeConn = closeConn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +74,6 @@ public class QueryMapper implements JdbcMapper {
|
|||||||
protected QueryMapper() {
|
protected QueryMapper() {
|
||||||
this.cm = null;
|
this.cm = null;
|
||||||
this.conn = null;
|
this.conn = null;
|
||||||
this.context = null;
|
|
||||||
this.closeConn = false;
|
this.closeConn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +81,6 @@ public class QueryMapper implements JdbcMapper {
|
|||||||
public void close() {
|
public void close() {
|
||||||
if (closeConn) {
|
if (closeConn) {
|
||||||
tryClose(conn);
|
tryClose(conn);
|
||||||
tryClose(context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import static com.moparisthebest.jdbc.QueryMapperTest.*;
|
|||||||
* Created by mopar on 7/1/17.
|
* Created by mopar on 7/1/17.
|
||||||
*/
|
*/
|
||||||
public class QueryRunnerTest {
|
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
|
@Override
|
||||||
public Connection create() throws SQLException {
|
public Connection create() throws SQLException {
|
||||||
return QueryMapperTest.getConnection();
|
return QueryMapperTest.getConnection();
|
||||||
|
Loading…
Reference in New Issue
Block a user