001package org.itsallcode.jdbc;
002
003import java.sql.Connection;
004
005import org.itsallcode.jdbc.batch.*;
006import org.itsallcode.jdbc.dialect.DbDialect;
007import org.itsallcode.jdbc.metadata.DbMetaData;
008import org.itsallcode.jdbc.resultset.RowMapper;
009import org.itsallcode.jdbc.resultset.SimpleResultSet;
010import org.itsallcode.jdbc.resultset.generic.Row;
011
012/**
013 * A simplified version of a JDBC {@link Connection}. Create new connections
014 * with
015 * <ul>
016 * <li>{@link ConnectionFactory#create(String, String, String)}</li>
017 * <li>or {@link DataSourceConnectionFactory#getConnection()}</li>
018 * <li>or {@link #wrap(Connection, DbDialect)}</li>
019 * </ul>
020 */
021public class SimpleConnection implements DbOperations {
022
023    private Transaction transaction;
024
025    private final ConnectionWrapper connection;
026
027    SimpleConnection(final Connection connection, final Context context, final DbDialect dialect) {
028        this.connection = new ConnectionWrapper(connection, context, dialect);
029    }
030
031    /**
032     * Wrap an existing {@link Connection} with a {@link SimpleConnection}.
033     * <p>
034     * Note: Calling {@link #close()} will close the underlying connection.
035     * 
036     * @param connection existing connection
037     * @param dialect    database dialect
038     * @return wrapped connection
039     */
040    public static SimpleConnection wrap(final Connection connection, final DbDialect dialect) {
041        return new SimpleConnection(connection, Context.builder().build(), dialect);
042    }
043
044    /**
045     * Start a new {@link Transaction} by disabling auto commit if necessary.
046     * <p>
047     * <em>Important:</em> The transaction must be committed or rolled back before
048     * the connection can be used again.
049     * 
050     * @return new transaction
051     */
052    public Transaction startTransaction() {
053        checkOperationAllowed();
054        transaction = Transaction.start(this.connection, tx -> {
055            if (this.transaction != tx) {
056                throw new IllegalStateException("Transaction not allowed to commit or rollback another transaction");
057            }
058            this.transaction = null;
059        });
060        return transaction;
061    }
062
063    private void checkOperationAllowed() {
064        if (transaction != null) {
065            throw new IllegalStateException("Operation not allowed on connection when transaction is active");
066        }
067        if (this.connection.isClosed()) {
068            throw new IllegalStateException("Operation not allowed on closed connection");
069        }
070    }
071
072    @Override
073    public int executeUpdate(final String sql) {
074        checkOperationAllowed();
075        return connection.executeUpdate(sql);
076    }
077
078    @Override
079    public void executeScript(final String sqlScript) {
080        checkOperationAllowed();
081        connection.executeScript(sqlScript);
082    }
083
084    @Override
085    public int executeUpdate(final String sql, final PreparedStatementSetter preparedStatementSetter) {
086        checkOperationAllowed();
087        return connection.executeUpdate(sql, preparedStatementSetter);
088    }
089
090    @Override
091    public SimpleResultSet<Row> query(final String sql) {
092        checkOperationAllowed();
093        return connection.query(sql);
094    }
095
096    @Override
097    public <T> SimpleResultSet<T> query(final String sql, final PreparedStatementSetter preparedStatementSetter,
098            final RowMapper<T> rowMapper) {
099        checkOperationAllowed();
100        return connection.query(sql, preparedStatementSetter, rowMapper);
101    }
102
103    @Override
104    public StatementBatchBuilder statementBatch() {
105        checkOperationAllowed();
106        return connection.statementBatch();
107    }
108
109    @Override
110    public PreparedStatementBatchBuilder preparedStatementBatch() {
111        checkOperationAllowed();
112        return connection.preparedStatementBatch();
113    }
114
115    @Override
116    public <T> RowPreparedStatementBatchBuilder<T> preparedStatementBatch(final Class<T> rowType) {
117        checkOperationAllowed();
118        return connection.rowPreparedStatementBatch();
119    }
120
121    /**
122     * Get database metadata.
123     * 
124     * @return metadata
125     */
126    public DbMetaData getMetaData() {
127        checkOperationAllowed();
128        return connection.getMetaData();
129    }
130
131    public Connection getOriginalConnection() {
132        checkOperationAllowed();
133        return connection.getOriginalConnection();
134    }
135
136    /**
137     * Close the underlying {@link Connection}.
138     * 
139     * @see Connection#close()
140     */
141    @Override
142    public void close() {
143        connection.close();
144    }
145}