1 package com.atlassian.sal.api.rdbms;
2
3 import com.atlassian.annotations.PublicApi;
4 import com.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 }