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}