diff -ruN src.orig/net/lemnik/eodsql/impl/BaseQueryImpl.java src/net/lemnik/eodsql/impl/BaseQueryImpl.java --- src.orig/net/lemnik/eodsql/impl/BaseQueryImpl.java 2010-07-07 13:03:41.000000000 +0200 +++ src/net/lemnik/eodsql/impl/BaseQueryImpl.java 2010-07-07 13:00:57.000000000 +0200 @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import java.util.HashSet; @@ -21,6 +22,7 @@ import javax.sql.DataSource; import net.lemnik.eodsql.BaseQuery; +import net.lemnik.eodsql.EoDException; import net.lemnik.eodsql.QueryTool; import net.lemnik.eodsql.InvalidQueryException; @@ -38,12 +40,10 @@ protected Map methods = new HashMap(); - private ConnectionSource connectionSource; + protected ConnectionSource connectionSource; private ExceptionTranslator exceptionTranslator; - private volatile boolean closed = false; - BaseQueryImpl( final ConnectionSource connectionSource, final Class clazz) { @@ -62,14 +62,15 @@ } protected void close() throws SQLException { - if(!closed) { - closed = true; - connectionSource.close(); - } + connectionSource.close(); } protected boolean isClosed() { - return closed; + try { + return connectionSource.isClosed(); + } catch (SQLException ex) { + throw new EoDException(ex); + } } private Set getParentInterfaces(final Class base) { @@ -170,6 +171,11 @@ } } + protected Context createContext(Annotation annotation, final Object[] args) + { + return new Context(annotation, args); + } + static interface Callable { public Object invoke(Method method, Object[] args) throws Throwable; @@ -212,8 +218,7 @@ final Object[] args) throws Throwable { - final Context context = - new Context(annotation, args); + final Context context = createContext(annotation, args); final Resource connection = new ConnectionSourceConnectionResource(connectionSource); @@ -229,7 +234,7 @@ } } } - + AbstractMethodImplementation getMethodImpl() { return (AbstractMethodImplementation) implementation; @@ -244,6 +249,8 @@ public void releaseConnection(Connection connection) throws SQLException; public void close() throws SQLException; + + public boolean isClosed() throws SQLException; } @@ -280,32 +287,42 @@ connection.close(); } + public boolean isClosed() throws SQLException { + return connection.isClosed(); + } + } static class DataSourceConnectionSource implements ConnectionSource { - private static final ThreadLocal CONNECTION_UTILS = + private final ThreadLocal CONNECTION_UTILS = new ThreadLocal(); private final DataSource datasource; - private final Set connections = Collections.synchronizedSet( - new HashSet()); + private final boolean autoCommit; + + // Dummy value to associate with an Object in the backing Map + private static final Object PRESENT = new Object(); + + private final Map connections = Collections.synchronizedMap( + new IdentityHashMap()); - DataSourceConnectionSource(final DataSource datasource) { + DataSourceConnectionSource(final DataSource datasource, final boolean autoCommit) { this.datasource = datasource; + this.autoCommit = autoCommit; } @Override protected void finalize() throws Throwable { - super.finalize(); close(); + super.finalize(); } public void close() throws SQLException { // when closing, we want exclusive access synchronized(connections) { - final Iterator it = connections.iterator(); + final Iterator it = connections.keySet().iterator(); while(it.hasNext()) { it.next().close(); @@ -314,12 +331,22 @@ } } - public Connection getConnection() throws SQLException { + public boolean isClosed() throws SQLException { + return connections.isEmpty(); + } + + public Connection getConnection() throws SQLException { ConnectionUtil util = CONNECTION_UTILS.get(); if(util == null || util.connection.isClosed()) { final Connection tmp = datasource.getConnection(); - connections.add(tmp); + try { + tmp.setAutoCommit(autoCommit); + } catch(SQLException ex) { + throw new EoDException("Cannot set auto-commit on Connection", ex); + } + + connections.put(tmp, PRESENT); util = new ConnectionUtil(tmp); util.count.incrementAndGet(); @@ -334,7 +361,7 @@ public void releaseConnection(final Connection connection) throws SQLException { - if(connections.contains(connection)) { + if(connections.containsKey(connection)) { final ConnectionUtil util = CONNECTION_UTILS.get(); if(util.count.decrementAndGet() <= 0) { @@ -362,6 +389,7 @@ } } + } private static class ConnectionSourceConnectionResource implements Resource { diff -ruN src.orig/net/lemnik/eodsql/impl/DefaultQueryFactory.java src/net/lemnik/eodsql/impl/DefaultQueryFactory.java --- src.orig/net/lemnik/eodsql/impl/DefaultQueryFactory.java 2010-07-07 00:28:22.000000000 +0200 +++ src/net/lemnik/eodsql/impl/DefaultQueryFactory.java 2010-07-07 13:00:31.000000000 +0200 @@ -103,15 +103,9 @@ final Class query, final ClassLoader loader) throws InvalidQueryException { if(TransactionQuery.class.isAssignableFrom(query)) { - try { - return constructProxy(loader, query, new TransactionQueryImpl( - dataSource.getConnection(), query)); - } catch(SQLException ex) { - throw new InvalidQueryException("Couldn't get a Connection from the DataSource " + - "for a TransactionQuery", ex); - } + return constructProxy(loader, query, new TransactionQueryImpl(dataSource, query)); } else { - return construct(new BaseQueryImpl.DataSourceConnectionSource(dataSource), query, loader); + return construct(new BaseQueryImpl.DataSourceConnectionSource(dataSource, true), query, loader); } } diff -ruN src.orig/net/lemnik/eodsql/impl/TransactionQueryImpl.java src/net/lemnik/eodsql/impl/TransactionQueryImpl.java --- src.orig/net/lemnik/eodsql/impl/TransactionQueryImpl.java 2010-07-07 00:28:23.000000000 +0200 +++ src/net/lemnik/eodsql/impl/TransactionQueryImpl.java 2010-07-07 13:00:57.000000000 +0200 @@ -1,59 +1,97 @@ package net.lemnik.eodsql.impl; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.sql.Savepoint; import java.sql.Connection; import java.sql.SQLException; +import javax.sql.DataSource; + import net.lemnik.eodsql.BaseQuery; -import net.lemnik.eodsql.InvalidQueryException; +import net.lemnik.eodsql.EoDException; import net.lemnik.eodsql.TransactionQuery; +import net.lemnik.eodsql.spi.Context; /** * Created on 2008/07/23 * @author Jason Morris */ class TransactionQueryImpl extends BaseQueryImpl { - protected final Connection connection; + final boolean noAutoClose; public TransactionQueryImpl(final Connection connection, final Class clazz) { super(new SingleConnectionSource(connection), clazz, TransactionQuery.class); - this.connection = connection; + this.noAutoClose = false; addTransactionMethods(); try { connection.setAutoCommit(false); } catch(SQLException ex) { - throw new InvalidQueryException("Cannot set auto-commit on Connection", ex); + throw new EoDException("Cannot set auto-commit on Connection", ex); } } - @Override - protected void close() throws SQLException { - super.close(); + public TransactionQueryImpl(final DataSource datasource, + final Class clazz) { + + super(new DataSourceConnectionSource(datasource, false), clazz, TransactionQuery.class); + this.noAutoClose = true; + addTransactionMethods(); } + @Override + protected Context createContext(Annotation annotation, final Object[] args) + { + final Context context = new Context(annotation, args); + if (noAutoClose) + { + context.setDontCloseConnection(true); + } + return context; + } + protected void commit() throws SQLException { - connection.commit(); + final Connection conn = connectionSource.getConnection(); + conn.commit(); + connectionSource.releaseConnection(conn); } protected void rollback() throws SQLException { - connection.rollback(); + final Connection conn = connectionSource.getConnection(); + conn.rollback(); + connectionSource.releaseConnection(conn); } protected void rollback(Savepoint savepoint) throws SQLException { - connection.rollback(savepoint); + final Connection conn = connectionSource.getConnection(); + conn.rollback(savepoint); + connectionSource.releaseConnection(conn); } protected Savepoint setSavepoint() throws SQLException { - return connection.setSavepoint(); + final Connection conn = connectionSource.getConnection(); + try + { + return conn.setSavepoint(); + } finally + { + connectionSource.releaseConnection(conn); + } } protected Savepoint setSavepoint(String name) throws SQLException { - return connection.setSavepoint(name); + final Connection conn = connectionSource.getConnection(); + try + { + return conn.setSavepoint(name); + } finally + { + connectionSource.releaseConnection(conn); + } } protected void addTransactionMethods() { diff -ruN src.orig/net/lemnik/eodsql/spi/Context.java src/net/lemnik/eodsql/spi/Context.java --- src.orig/net/lemnik/eodsql/spi/Context.java 2010-07-07 00:28:22.000000000 +0200 +++ src/net/lemnik/eodsql/spi/Context.java 2010-07-07 13:00:31.000000000 +0200 @@ -1,9 +1,8 @@ package net.lemnik.eodsql.spi; -import java.lang.reflect.Method; - import java.lang.annotation.Annotation; +import java.sql.Connection; import java.sql.SQLException; import java.util.Map; @@ -53,6 +52,8 @@ private boolean autoclose = true; + private boolean dontCloseConnection = false; + private boolean closed = false; /** @@ -80,7 +81,10 @@ @Override protected void finalize() throws Exception { - close(); + if (autoclose) + { + close(); + } } /** @@ -201,8 +205,21 @@ return autoclose; } + public boolean isDontCloseConnection() { + return dontCloseConnection; + } + + public void setDontCloseConnection(boolean closeExceptConnection) { + this.dontCloseConnection = closeExceptConnection; + } + + private final boolean isConnectionResource(Resource r) + { + return r.getResourceType() == Connection.class; + } + /** - * Explicidly close this {@code Context}. This method needs to be invoked to close any + * Explicitly close this {@code Context}. This method needs to be invoked to close any * remaining {@code Resource}s that this {@code Context} still holds if the {@code Context} * is not in {@link #setAutoclose(boolean) auto-close} mode. This method is a no-op if the * {@code Context} is already closed. @@ -226,6 +243,10 @@ while(iterator.hasPrevious()) { final Resource r = iterator.previous(); + if (dontCloseConnection && isConnectionResource(r)) + { + continue; + } // close each of the Resource objects if(!r.isClosed()) {