View Javadoc
1   package com.atlassian.sal.api.rdbms;
2   
3   import com.atlassian.annotations.PublicApi;
4   import io.atlassian.fugue.Option;
5   
6   import javax.annotation.Nonnull;
7   
8   /**
9    * Interface allowing the user to execute a callback with a {@link java.sql.Connection}, in the context of a transaction.
10   * <p>
11   * Note that participation in an existing transaction i.e. <code>requiresNew</code> is merely a hint to the host
12   * application and not binding. Best effort will be attempted.
13   * <p>
14   * The following example will query an application's table using JDBC:
15   * <pre>
16   * {@code
17   * final TransactionalExecutor transactionalExecutor = transactionalExecutorFactory
18   *         .createExecutor()
19   *         .readOnly()
20   *         .newTransaction();
21   *
22   * final String summary = transactionalExecutor.execute(new ConnectionCallback<String>()
23   * {
24   *     public String execute(final Connection connection)
25   *     {
26   *         try
27   *         {
28   *             final String schemaPrefix = transactionalExecutor.getSchemaName().map(s -> s + '.').getOrElse("");
29   *
30   *             final PreparedStatement preparedStatement = connection.prepareStatement(""
31   *                     + "select summary\n"
32   *                     + "from " + schemaPrefix + "jiraissue\n"
33   *                     + "order by id desc");
34   *
35   *             final ResultSet resultSet = preparedStatement.executeQuery();
36   *
37   *             return resultSet.next() ? resultSet.getString("summary") : "no issues";
38   *         }
39   *         catch (SQLException e)
40   *         {
41   *             throw new RuntimeException("oh noes!", e);
42   *         }
43   *     }
44   * });
45   * }
46   * </pre>
47   *
48   * @see com.atlassian.sal.api.rdbms.TransactionalExecutorFactory
49   * @since 3.0
50   */
51  @PublicApi
52  public interface TransactionalExecutor {
53      /**
54       * Execute a callback which is supplied a {@link java.sql.Connection}, within a transaction.
55       * <p>
56       * After successful execution, the transaction will be committed. If the transaction is within the scope of a larger
57       * transaction i.e. <code>requiresNew</code> has been set, it will be scheduled for commit, pending successful
58       * completion of the target transaction.
59       * <p>
60       * If any exception is thrown by <code>callback</code>, the transaction will be immediately rolled back.
61       * <p>
62       * Do not attempt to retain or reuse the Connection outside of the scope of <code>callback</code> or within a
63       * different thread. Connection is usually not considered thread safe.
64       * <p>
65       * Exceptions thrown from the callback are passed through without wrapping.
66       * {@link java.sql.SQLException} occuring in the Executor will be wrapped in {@link com.atlassian.sal.api.rdbms.RdbmsException}.
67       * <p>
68       * A {@link java.lang.UnsupportedOperationException} will be thrown on invoking any of the following:
69       * <ul>
70       * <li>{@link java.sql.Connection#setAutoCommit(boolean)}</li>
71       * <li>{@link java.sql.Connection#commit()}</li>
72       * <li>{@link java.sql.Connection#close()}</li>
73       * <li>{@link java.sql.Connection#rollback()}</li>
74       * <li>{@link java.sql.Connection#setReadOnly(boolean)}</li>
75       * <li>{@link java.sql.Connection#setReadOnly(boolean)}</li>
76       * <li>{@link java.sql.Connection#abort(java.util.concurrent.Executor)}</li>
77       * <li>{@link java.sql.Connection#setCatalog(String)}</li>
78       * <li>{@link java.sql.Connection#setSchema(String)}</li>
79       * <li>{@link java.sql.Connection#setTransactionIsolation(int)}</li>
80       * <li>{@link java.sql.Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}</li>
81       * </ul>
82       *
83       * @param callback mandatory, may return {@link java.lang.Void}
84       * @return return value of <code>callback</code>
85       */
86      <A> A execute(@Nonnull ConnectionCallback<A> callback);
87  
88      /**
89       * Returns the configured schema name (if any), for connections provided during {@link #execute(ConnectionCallback)}.
90       *
91       * @return schema name, if there is one
92       */
93      @Nonnull
94      Option<String> getSchemaName();
95  
96      /**
97       * Alter this executor so that the connection is read-only
98       *
99       * @return this executor
100      */
101     @Nonnull
102     TransactionalExecutor readOnly();
103 
104     /**
105      * Alter this executor so that the connection is read-write
106      *
107      * @return this executor
108      */
109     @Nonnull
110     TransactionalExecutor readWrite();
111 
112     /**
113      * Alter this executor so that it executes in a new transaction, regardless of any existing
114      *
115      * @return this executor
116      */
117     @Nonnull
118     TransactionalExecutor newTransaction();
119 
120     /**
121      * Alter this executor so that the connection executes within an existing transaction or creates a new one if one is
122      * not present
123      *
124      * @return this executor
125      */
126     @Nonnull
127     TransactionalExecutor existingTransaction();
128 }