Hi digulla,
Factory wouldn't need to extend Connection, it could simply provide
the same method signatures and delegate to the underlying connection.
Take a look at the attached class that does this. Ideally this
functionality should be built into Factory instead of having to wrap it
with a second class.
Gili
On 13/06/2012 1:48 PM, digulla wrote:
Am Montag, 11. Juni 2012 21:16:56 UTC+2 schrieb Gili Tzabari:
What about committing transactions? If I have to invoke
Factory.getConnection().commit() after the JOOQ code I still end
up having to catch both DataAccessException and SQLException.
Couldn't Factory.getConnection() return a wrapper object?
Not really. There is no sane way to extend most JDBC classes in Java
code - compilation will break with different versions of Java, for
example.
I would prefer if "new Factory()" would accept a wrapper object
instead; that would ease the migration path to new versions of JDBC
and make the API cleaner. If this wrapper would have this API:
getConnection() // get underlying JDBC connection
commit() // commit the current transaction
rollback() // ...
then such things would be possible.
package com.vetailr.service;
import com.google.inject.Inject;
import com.google.inject.servlet.RequestScoped;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.jooq.Attachable;
import org.jooq.Batch;
import org.jooq.BatchBindStep;
import org.jooq.Condition;
import org.jooq.Cursor;
import org.jooq.DeleteQuery;
import org.jooq.DeleteWhereStep;
import org.jooq.FactoryOperations;
import org.jooq.Field;
import org.jooq.Insert;
import org.jooq.InsertQuery;
import org.jooq.InsertSetStep;
import org.jooq.InsertValuesStep;
import org.jooq.LoaderOptionsStep;
import org.jooq.MergeUsingStep;
import org.jooq.Query;
import org.jooq.QueryPart;
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.ResultQuery;
import org.jooq.SQLDialect;
import org.jooq.Schema;
import org.jooq.SchemaMapping;
import org.jooq.Select;
import org.jooq.SelectQuery;
import org.jooq.SelectSelectStep;
import org.jooq.Sequence;
import org.jooq.SimpleSelectQuery;
import org.jooq.SimpleSelectWhereStep;
import org.jooq.Table;
import org.jooq.TableLike;
import org.jooq.TableRecord;
import org.jooq.Truncate;
import org.jooq.UDT;
import org.jooq.UDTRecord;
import org.jooq.UpdatableRecord;
import org.jooq.UpdateQuery;
import org.jooq.UpdateSetStep;
import org.jooq.conf.Settings;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.Factory;
/**
* An abstraction of a database connection.
* <p/>
* @author Gili Tzabari
*/
@RequestScoped
public class DatabaseConnection implements FactoryOperations
{
private static final long serialVersionUID = 1L;
private final Factory database;
@Inject
public DatabaseConnection(Factory database)
{
this.database = database;
}
/**
* Sets this session's auto-commit mode to the given state. If a
session is in auto-commit mode,
* then all its SQL statements will be executed and committed as
individual transactions.
* Otherwise, its SQL statements are grouped into transactions that are
terminated by a call to
* either the method
* <code>commit</code> or the method
* <code>rollback</code>. By default, new sessions are in auto-commit
mode. <P> The commit occurs
* when the statement completes. The time when the statement completes
depends on the type of SQL
* Statement: <ul> <li>For DML statements, such as Insert, Update or
Delete, and DDL statements,
* the statement is complete as soon as it has finished executing.
<li>For Select statements, the
* statement is complete when the associated result set is closed.
<li>For
* <code>CallableStatement</code> objects or for statements that return
multiple results, the
* statement is complete when all of the associated result sets have
been closed, and all update
* counts and output parameters have been retrieved. </ul> <P>
<B>NOTE:</B> If this method is
* called during a transaction and the auto-commit mode is changed, the
transaction is committed.
* If
* <code>setAutoCommit</code> is called and the auto-commit mode is not
changed, the call is a
* no-op.
* <p/>
* @param autoCommit <code>true</code> to enable auto-commit mode;
<code>false</code> to disable
* it
* @throws DataAccessException if a database access error occurs,
setAutoCommit(true) is called
* while participating in a distributed
transaction, or this method is
* called on a closed session
* @see #isAutoCommit
*/
public void setAutoCommit(boolean autoCommit)
{
try
{
database.getConnection().setAutoCommit(autoCommit);
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Indicates if the current auto-commit mode for this
* <code>DatabaseConnection</code> object.
* <p/>
* @return the current state of this <code>DatabaseConnection</code>
object's auto-commit mode
* @throws DataAccessException if a database access error occurs or
this method is called on a
* closed session
* @see #setAutoCommit
*/
public boolean isAutoCommit()
{
try
{
return database.getConnection().getAutoCommit();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Makes all changes made since the previous commit/rollback permanent
and releases any database
* locks currently held by this
* <code>DatabaseConnection</code> object. This method should be used
only when auto-commit mode
* has been disabled.
* <p/>
* @throws DataAccessException if a database access error occurs, this
method is called while
* participating in a distributed
transaction, if this method is
* called on a closed session or the
<code>DatabaseConnection</code>
* object is in auto-commit mode.
* @see #setAutoCommit
*/
public void commit()
{
try
{
database.getConnection().commit();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Undoes all changes made in the current transaction and releases any
database locks currently
* held by the
* <code>DatabaseConnection</code> object. This method should be used
only when auto-commit mode
* has been disabled.
* <p/>
* @throws DataAccessException if a database access error occurs, this
method is called while
* participating in a distributed
transaction, this method is called
* on a closed session or the
<code>DatabaseConnection</code> object
* is in auto-commit mode
* @see #setAutoCommit
*/
public void rollback()
{
try
{
database.getConnection().rollback();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Releases the
* <code>DatabaseConnection</code> object's database and JDBC resources
immediately instead of
* waiting for them to be automatically released. <P> Calling the method
* <code>close</code> on a
* <code>DatabaseConnection</code> object that is already closed is a
no-op. <P> It is <b>strongly
* recommended</b> that an application explicitly commits or rolls back
an active transaction
* prior to calling the
* <code>close</code> method. If the
* <code>close</code> method is called and there is an active
transaction, the results are
* implementation-defined. <P>
* <p/>
* @throws DataAccessException if a database access error occurs
*/
public void close()
{
try
{
database.getConnection().close();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Indicates whether this
* <code>DatabaseConnection</code> object has been closed. A session is
closed if the method
* <code>close</code> has been called on it or if certain fatal errors
have occurred. This method
* is guaranteed to return
* <code>true</code> only when it is called after the method
* <code>DatabaseConnection.close</code> has been called. <P> This
method generally cannot be
* called to determine whether a connection to a database is valid or
invalid. A typical client
* can determine that a connection is invalid by catching any
exceptions that might be thrown when
* an operation is attempted.
* <p/>
* @return <code>true</code> if this <code>DatabaseConnection</code>
object is
* closed; <code>false</code> if it is still open
* @throws DataAccessException if a database access error occurs
*/
public boolean isClosed()
{
try
{
return database.getConnection().isClosed();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Puts this session in read-only mode as a hint to the driver to
enable database optimizations.
* <p/>
* <P><B>Note:</B> This method cannot be called during a transaction.
* <p/>
* @param readOnly <code>true</code> enables read-only mode;
<code>false</code> disables it
* @throws DataAccessException if a database access error occurs, this
method is called on a
* closed session or this method is called
during a transaction
*/
public void setReadOnly(boolean readOnly)
{
try
{
database.getConnection().isClosed();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Retrieves whether this
* <code>DatabaseConnection</code> object is in read-only mode.
* <p/>
* @return <code>true</code> if this <code>DatabaseConnection</code>
object is * read-only; <code>false</code> otherwise
* @throws DataAccessException SQLException if a database access error
occurs or this method is
* called on a closed session
*/
public boolean isReadOnly()
{
try
{
return database.getConnection().isReadOnly();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Sets the given catalog name in order to select a subspace of this
* <code>DatabaseConnection</code> object's database in which to work.
<P> If the driver does not
* support catalogs, it will silently ignore this request.
* <p/>
* @param catalog the name of a catalog (subspace in this
<code>DatabaseConnection</code> object's
* database) in which to work
* @throws DataAccessException if a database access error occurs or
this method is called on a
* closed session
* @see #getCatalog
*/
public void setCatalog(String catalog)
{
try
{
database.getConnection().setCatalog(catalog);
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Retrieves this
* <code>DatabaseConnection</code> object's current catalog name.
* <p/>
* @return the current catalog name or <code>null</code> if there is
none
* @throws DataAccessException if a database access error occurs or
this method is called on a
* closed session
* @see #setCatalog
*/
public String getCatalog()
{
try
{
return database.getConnection().getCatalog();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Attempts to change the transaction isolation level for this
* <code>DatabaseConnection</code> object to the one given. <P>
<B>Note:</B> If this method is
* called during a transaction, the result is implementation-defined.
* <p/>
* @param level the transaction isolation level. (Note that
<code>TransactionIsolation.NONE</code>
* cannot be used because it specifies that transactions
are not supported.)
* @throws DataAccessException if a database access error occurs, this
method is called on a
* closed session
* @see DatabaseMetaData#supportsTransactionIsolationLevel
* @see #getTransactionIsolation
*/
public void setTransactionIsolation(TransactionIsolation level)
{
try
{
database.getConnection().setTransactionIsolation(level.getId());
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Retrieves this
* <code>DatabaseConnection</code> object's current transaction
isolation level.
* <p/>
* @return the current transaction isolation level
* @throws DataAccessException if a database access error occurs or
this method is called on a
* closed session
* @see #setTransactionIsolation
*/
public TransactionIsolation getTransactionIsolation()
{
try
{
return
TransactionIsolation.fromConnection(database.getConnection().getTransactionIsolation());
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Retrieves the first warning reported by calls on this
* <code>DatabaseConnection</code> object. If there is more than one
warning, subsequent warnings
* will be chained to the first one and can be retrieved by calling the
method
* <code>SQLWarning.getNextWarning</code> on the warning that was
retrieved previously. <P> This
* method may not be called on a closed session; doing so will cause a
* <code>DataAccessException</code> to be thrown.
* <p/>
* <P><B>Note:</B> Subsequent warnings will be chained to this
SQLWarning.
* <p/>
* @return the first <code>SQLWarning</code> object or
<code>null</code> if there are none
* @throws DataAccessException if a database access error occurs or
this method is called on a
* closed session
* @see SQLWarning
*/
public SQLWarning getWarnings()
{
try
{
return database.getConnection().getWarnings();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Clears all warnings reported for this
* <code>DatabaseConnection</code> object. After a call to this method,
the method
* <code>getWarnings</code> returns
* <code>null</code> until a new warning is reported for this
* <code>DatabaseConnection</code> object.
* <p/>
* @throws DataAccessException SQLException if a database access error
occurs or this method is
* called on a closed session
*/
public void clearWarnings()
{
try
{
database.getConnection().clearWarnings();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Returns true if the session has not been closed and is still valid.
The driver shall submit a
* query on the session or use some other mechanism that positively
verifies the session is still
* valid when this method is called. <p> The query submitted by the
driver to validate the session
* shall be executed in the context of the current transaction.
* <p/>
* @param timeout The time in seconds to wait for the database
operation used to validate the
* session to complete. If the timeout period expires
before the operation
* completes, this method returns false. A value of 0
indicates a timeout is not
* applied to the database operation.
* @return true if the session is valid, false otherwise
* @throws DataAccessException if the value supplied for
<code>timeout</code> is less then 0
* @since 1.6
* @see java.sql.DatabaseMetaData#getClientInfoProperties
*/
public boolean isValid(int timeout)
{
try
{
return database.getConnection().isValid(timeout);
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
/**
* Constructs an object that implements the
* <code>Blob</code> interface. The object returned initially contains
no data. The
* <code>setBinaryStream</code> and
* <code>setBytes</code> methods of the
* <code>Blob</code> interface may be used to add data to the
* <code>Blob</code>.
* <p/>
* @return An object that implements the <code>Blob</code> interface
* @throws DataAccessException if an object that implements the
<code>Blob</code> interface can
* not be constructed, this method is
called on a closed connection, a
* database access error occurs or if the
JDBC driver does not support
* this data type
* @since 1.6
*/
public Blob createBlob()
{
try
{
return database.getConnection().createBlob();
}
catch (SQLException e)
{
throw new DataAccessException("", e);
}
}
@Override
public final SQLDialect getDialect()
{
return database.getDialect();
}
@Override
public final Connection getConnection()
{
return database.getConnection();
}
@Override
public final void setConnection(Connection connection)
{
database.setConnection(connection);
}
@Override
@SuppressWarnings("deprecation")
public final SchemaMapping getSchemaMapping()
{
return database.getSchemaMapping();
}
@Override
public final Settings getSettings()
{
return database.getSettings();
}
@Override
public final Map<String, Object> getData()
{
return database.getData();
}
@Override
public final Object getData(String key)
{
return database.getData(key);
}
@Override
public final Object setData(String key, Object value)
{
return database.setData(key, value);
}
@Override
public final String render(QueryPart part)
{
return database.render(part);
}
@Override
public final String renderNamedParams(QueryPart part)
{
return database.renderNamedParams(part);
}
@Override
public final String renderInlined(QueryPart part)
{
return database.renderInlined(part);
}
@Override
public final void attach(Attachable... attachables)
{
database.attach(attachables);
}
@Override
public final void attach(Collection<Attachable> attachables)
{
database.attach(attachables);
}
@Override
public final <R extends TableRecord<R>> LoaderOptionsStep<R>
loadInto(Table<R> table)
{
return database.loadInto(table);
}
@Override
public final Query query(String sql)
{
return database.query(sql);
}
@Override
public final Query query(String sql, Object... bindings)
{
return database.query(sql, bindings);
}
@Override
public final Result<Record> fetch(String sql)
{
return database.fetch(sql);
}
@Override
public final Result<Record> fetch(String sql, Object... bindings)
{
return database.fetch(sql, bindings);
}
@Override
public final Cursor<Record> fetchLazy(String sql) throws
DataAccessException
{
return database.fetchLazy(sql);
}
@Override
public final Cursor<Record> fetchLazy(String sql, Object... bindings)
throws DataAccessException
{
return database.fetchLazy(sql, bindings);
}
@Override
public final List<Result<Record>> fetchMany(String sql)
{
return database.fetchMany(sql);
}
@Override
public final List<Result<Record>> fetchMany(String sql, Object...
bindings)
{
return database.fetchMany(sql, bindings);
}
@Override
public final Record fetchOne(String sql)
{
return database.fetchOne(sql);
}
@Override
public final Record fetchOne(String sql, Object... bindings)
{
return database.fetchOne(sql, bindings);
}
@Override
public final int execute(String sql) throws DataAccessException
{
return database.execute(sql);
}
@Override
public final int execute(String sql, Object... bindings) throws
DataAccessException
{
return database.execute(sql, bindings);
}
@Override
public final ResultQuery<Record> resultQuery(String sql) throws
DataAccessException
{
return database.resultQuery(sql);
}
@Override
public final ResultQuery<Record> resultQuery(String sql, Object...
bindings) throws
DataAccessException
{
return database.resultQuery(sql, bindings);
}
@Override
public final Result<Record> fetch(ResultSet rs)
{
return database.fetch(rs);
}
@Override
public final <R extends Record> SimpleSelectWhereStep<R>
selectFrom(Table<R> table)
{
return database.selectFrom(table);
}
@Override
public final SelectSelectStep select(Field<?>... fields)
{
return database.select(fields);
}
@Override
public final SelectSelectStep selectZero()
{
return database.selectZero();
}
@Override
public final SelectSelectStep selectOne()
{
return database.selectOne();
}
@Override
public final SelectSelectStep selectCount()
{
return database.selectCount();
}
@Override
public final SelectSelectStep selectDistinct(Field<?>... fields)
{
return database.selectDistinct(fields);
}
@Override
public final SelectSelectStep select(Collection<? extends Field<?>>
fields)
{
return database.select(fields);
}
@Override
public final SelectSelectStep selectDistinct(Collection<? extends
Field<?>> fields)
{
return database.selectDistinct(fields);
}
@Override
public final SelectQuery selectQuery()
{
return database.selectQuery();
}
@Override
public final <R extends Record> SimpleSelectQuery<R>
selectQuery(TableLike<R> table)
{
return database.selectQuery(table);
}
@Override
public final <R extends Record> InsertQuery<R> insertQuery(Table<R>
into)
{
return database.insertQuery(into);
}
@Override
public final <R extends Record> InsertSetStep<R> insertInto(Table<R>
into)
{
return database.insertInto(into);
}
@Override
public final <R extends Record> InsertValuesStep<R> insertInto(Table<R>
into,
Field<?>... fields)
{
return database.insertInto(into, fields);
}
@Override
public final <R extends Record> InsertValuesStep<R> insertInto(Table<R>
into,
Collection<? extends Field<?>> fields)
{
return database.insertInto(into, fields);
}
@Override
@SuppressWarnings("deprecation")
public final <R extends Record> Insert<R> insertInto(Table<R> into,
Select<?> select)
{
return database.insertInto(into, select);
}
@Override
public final <R extends Record> UpdateQuery<R> updateQuery(Table<R>
table)
{
return database.updateQuery(table);
}
@Override
public final <R extends Record> UpdateSetStep<R> update(Table<R> table)
{
return database.update(table);
}
@Override
public final <R extends Record> MergeUsingStep<R> mergeInto(Table<R>
table)
{
return database.mergeInto(table);
}
@Override
public final <R extends Record> DeleteQuery<R> deleteQuery(Table<R>
table)
{
return database.deleteQuery(table);
}
@Override
public final <R extends Record> DeleteWhereStep<R> delete(Table<R>
table)
{
return database.delete(table);
}
@Override
public final Batch batch(Query... queries)
{
return database.batch(queries);
}
@Override
public final Batch batch(Collection<? extends Query> queries)
{
return database.batch(queries);
}
@Override
public final BatchBindStep batch(Query query)
{
return database.batch(query);
}
@Override
public final Batch batchStore(UpdatableRecord<?>... records)
{
return database.batchStore(records);
}
@Override
public final <R extends TableRecord<R>> Truncate<R> truncate(Table<R>
table)
{
return database.truncate(table);
}
@Override
public final BigInteger lastID()
{
return database.lastID();
}
@Override
public final <T extends Number> T nextval(Sequence<T> sequence)
{
return database.nextval(sequence);
}
@Override
public final <T extends Number> T currval(Sequence<T> sequence)
{
return database.currval(sequence);
}
@Override
public final int use(Schema schema)
{
return database.use(schema);
}
@Override
public final int use(String schema)
{
return database.use(schema);
}
@Override
public final <R extends UDTRecord<R>> R newRecord(UDT<R> type)
{
return database.newRecord(type);
}
@Override
public final <R extends TableRecord<R>> R newRecord(Table<R> table)
{
return database.newRecord(table);
}
@Override
public final <R extends TableRecord<R>> R newRecord(Table<R> table,
Object source)
{
return database.newRecord(table, source);
}
@Override
public final <R extends Record> Result<R> fetch(Table<R> table)
{
return database.fetch(table);
}
@Override
public final <R extends Record> Result<R> fetch(Table<R> table,
Condition condition)
{
return database.fetch(table, condition);
}
@Override
public final <R extends Record> R fetchOne(Table<R> table)
{
return database.fetchOne(table);
}
@Override
public final <R extends Record> R fetchOne(Table<R> table, Condition
condition)
{
return database.fetchOne(table, condition);
}
@Override
public final <R extends Record> R fetchAny(Table<R> table)
{
return database.fetchAny(table);
}
@Override
public final <R extends TableRecord<R>> int executeInsert(Table<R>
table, R record)
{
return database.executeInsert(table, record);
}
@Override
public final <R extends TableRecord<R>> int executeUpdate(Table<R>
table, R record)
{
return database.executeUpdate(table, record);
}
@Override
public final <R extends TableRecord<R>, T> int executeUpdate(Table<R>
table, R record,
Condition condition)
{
return database.executeUpdate(table, record, condition);
}
@Override
public final <R extends TableRecord<R>> int executeUpdateOne(Table<R>
table, R record)
{
return database.executeUpdateOne(table, record);
}
@Override
public final <R extends TableRecord<R>, T> int
executeUpdateOne(Table<R> table, R record,
Condition condition)
{
return database.executeUpdateOne(table, record, condition);
}
@Override
public final <R extends TableRecord<R>> int executeDelete(Table<R>
table)
{
return database.executeDelete(table);
}
@Override
public final <R extends TableRecord<R>, T> int executeDelete(Table<R>
table, Condition condition)
{
return database.executeDelete(table, condition);
}
@Override
public final <R extends TableRecord<R>> int executeDeleteOne(Table<R>
table)
{
return database.executeDeleteOne(table);
}
@Override
public final <R extends TableRecord<R>, T> int
executeDeleteOne(Table<R> table,
Condition condition)
{
return database.executeDeleteOne(table, condition);
}
@Override
public String toString()
{
return database.toString();
}
}