Skip to content

Commit 4fd457e

Browse files
committed
give some love to org.hibernate.Transaction
also clean up some stuff in AbstractPersistentCollection Unfortunately TransactionStatus is an SPI type, which makes its use in the API of Transaction a bit wrong. Also, it's weird to see dependence on an interface defined by JPA. (However these are relatively harmless aesthetic points, so I decided not to deprecate anything.) Instead I've added alternative "convenience" operations. Note on terminology: a transaction which has been "marked for rollback only" is still an active transaction! You can still do work in it, and that work will still be atomically rolled back when the transaction completes. Apparently there was some confusion on this at some point, which was later fixed, but some evidence of the confusion was left behind in code.
1 parent d0b0c57 commit 4fd457e

18 files changed

+455
-203
lines changed

hibernate-core/src/main/java/org/hibernate/Transaction.java

Lines changed: 164 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
import org.hibernate.resource.transaction.spi.TransactionStatus;
1212

13+
import java.util.function.Consumer;
14+
15+
import static org.hibernate.resource.transaction.backend.jta.internal.StatusTranslator.translate;
16+
1317
/**
1418
* Represents a resource-local transaction, where <em>resource-local</em> is interpreted
1519
* by Hibernate to mean any transaction under the control of Hibernate. That is to say,
@@ -38,37 +42,188 @@
3842
*
3943
* @author Anton van Straaten
4044
* @author Steve Ebersole
45+
* @author Gavin King
4146
*
4247
* @see Session#beginTransaction()
4348
*/
4449
public interface Transaction extends EntityTransaction {
4550
/**
4651
* Get the current {@linkplain TransactionStatus status} of this transaction.
52+
*
53+
* @apiNote {@link TransactionStatus} belongs to an SPI package, and so this
54+
* operation is a (fairly harmless) layer-breaker. Prefer the use
55+
* of {@link #isActive}, {@link #isComplete}, {@link #wasStarted},
56+
* {@link #wasSuccessful}, {@link #wasFailure}, or
57+
* {@link #isInCompletionProcess} according to need.
58+
*
4759
*/
4860
TransactionStatus getStatus();
4961

5062
/**
51-
* Register a user {@link Synchronization synchronization callback} for this transaction.
63+
* Is this transaction still active?
64+
* <p>
65+
* A transaction which has been {@linkplain #markRollbackOnly marked for rollback}
66+
* is still considered active, and is still able to perform work. To determine if
67+
* a transaction has been marked for rollback, call {@link #getRollbackOnly()}.
68+
*
69+
* @return {@code true} if the {@linkplain #getStatus status}
70+
* is {@link TransactionStatus#ACTIVE} or
71+
* {@link TransactionStatus#MARKED_ROLLBACK}
72+
*/
73+
default boolean isActive() {
74+
return switch (getStatus()) {
75+
case ACTIVE, MARKED_ROLLBACK -> true;
76+
default -> false;
77+
};
78+
}
79+
80+
/**
81+
* Is this transaction currently in the completion process?
82+
* <p>
83+
* Note that a {@link Synchronization} is called <em>before</em> and <em>after</em>
84+
* the completion process. Therefore, this state is not usually observable to the
85+
* client program logic.
86+
*
87+
* @return {@code true} if the {@linkplain #getStatus status}
88+
* is {@link TransactionStatus#COMMITTING} or
89+
* {@link TransactionStatus#ROLLING_BACK}
90+
*
91+
* @since 7.0
92+
*/
93+
@Incubating
94+
default boolean isInCompletionProcess() {
95+
return switch (getStatus()) {
96+
case COMMITTING, ROLLING_BACK -> true;
97+
default -> false;
98+
};
99+
}
100+
101+
/**
102+
* Is this transaction complete?
103+
*
104+
* @return {@code true} if the {@linkplain #getStatus status}
105+
* is {@link TransactionStatus#COMMITTED},
106+
* {@link TransactionStatus#ROLLED_BACK},
107+
* {@link TransactionStatus#FAILED_COMMIT}, or
108+
* {@link TransactionStatus#FAILED_ROLLBACK}
109+
*
110+
* @since 7.0
111+
*/
112+
@Incubating
113+
default boolean isComplete() {
114+
return switch (getStatus()) {
115+
case COMMITTED, ROLLED_BACK, FAILED_COMMIT, FAILED_ROLLBACK -> true;
116+
default -> false;
117+
};
118+
}
119+
120+
/**
121+
* Was this transaction already started?
122+
*
123+
* @return {@code true} if the {@linkplain #getStatus status} is
124+
* anything other than {@link TransactionStatus#NOT_ACTIVE}
125+
*
126+
* @since 7.0
127+
*/
128+
@Incubating
129+
default boolean wasStarted() {
130+
return getStatus() != TransactionStatus.NOT_ACTIVE;
131+
}
132+
133+
/**
134+
* Was this transaction already successfully committed?
135+
*
136+
* @return {@code true} if the {@linkplain #getStatus status}
137+
* is {@link TransactionStatus#COMMITTED}
138+
*
139+
* @since 7.0
140+
*/
141+
@Incubating
142+
default boolean wasSuccessful() {
143+
return getStatus() == TransactionStatus.COMMITTED;
144+
}
145+
146+
/**
147+
* Was this transaction a failure? Here we consider a successful rollback,
148+
* a failed commit, or a failed rollback to amount to transaction failure.
149+
*
150+
* @return {@code true} if the {@linkplain #getStatus status}
151+
* is {@link TransactionStatus#ROLLED_BACK},
152+
* {@link TransactionStatus#FAILED_COMMIT},
153+
* {@link TransactionStatus#FAILED_ROLLBACK}
154+
*
155+
* @since 7.0
156+
*/
157+
@Incubating
158+
default boolean wasFailure() {
159+
return switch (getStatus()) {
160+
case ROLLED_BACK, FAILED_COMMIT, FAILED_ROLLBACK -> true;
161+
default -> false;
162+
};
163+
}
164+
165+
/**
166+
* Register an action which will be called during the "before completion" phase.
52167
*
53-
* @param synchronization The {@link Synchronization} callback to register.
168+
* @since 7.0
169+
*/
170+
@Incubating
171+
default void runBeforeCompletion(Runnable action) {
172+
registerSynchronization( new Synchronization() {
173+
@Override
174+
public void beforeCompletion() {
175+
action.run();
176+
}
177+
@Override
178+
public void afterCompletion(int status) {
179+
}
180+
} );
181+
}
182+
183+
/**
184+
* Register an action which will be called during the "after completion" phase.
54185
*
55-
* @throws HibernateException Indicates a problem registering the synchronization.
186+
* @since 7.0
187+
*/
188+
@Incubating
189+
default void runAfterCompletion(Consumer<TransactionStatus> action) {
190+
registerSynchronization( new Synchronization() {
191+
@Override
192+
public void beforeCompletion() {
193+
}
194+
@Override
195+
public void afterCompletion(int status) {
196+
action.accept( translate( status ) );
197+
}
198+
} );
199+
}
200+
201+
/**
202+
* Register a {@linkplain Synchronization synchronization callback} for this transaction.
203+
*
204+
* @param synchronization The {@link Synchronization} callback to register
205+
*
206+
* @apiNote {@link Synchronization} is a type defined by JTA, but this operation does
207+
* not depend on the use of JTA for transaction management. Prefer the use of
208+
* the methods {@link #runBeforeCompletion} and {@link #runAfterCompletion}
209+
* for convenience.
56210
*/
57211
void registerSynchronization(Synchronization synchronization);
58212

59213
/**
60-
* Set the transaction timeout for any transaction started by any subsequent call to
61-
* {@link #begin} on this instance.
214+
* Set the transaction timeout for any transaction started by a subsequent call to
215+
* {@link #begin} on this instance of {@code Transaction}.
62216
*
63-
* @param seconds The number of seconds before a timeout.
217+
* @param seconds The number of seconds before a timeout
64218
*/
65219
void setTimeout(int seconds);
66220

67221
/**
68-
* Retrieve the transaction timeout set for this instance. A negative integer indicates
69-
* that no timeout has been set.
222+
* Retrieve the transaction timeout set for this instance.
223+
* <p>
224+
* A {@code null} return value indicates that no timeout has been set.
70225
*
71-
* @return The timeout, in seconds.
226+
* @return the timeout, in seconds, or {@code null}
72227
*/
73228
@Nullable Integer getTimeout();
74229

hibernate-core/src/main/java/org/hibernate/cfg/TransactionSettings.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,10 @@ public interface TransactionSettings {
157157
*
158158
* @settingDefault {@code false} (disabled)
159159
*
160-
* @apiNote Generally speaking, all access to transactional data should be done in a transaction.
160+
* @apiNote Generally speaking, all access to transactional data should be
161+
* done in a transaction. Use of this setting is discouraged.
161162
*
163+
* @see org.hibernate.boot.spi.SessionFactoryOptions#isInitializeLazyStateOutsideTransactionsEnabled
162164
* @see org.hibernate.boot.SessionFactoryBuilder#applyLazyInitializationOutsideTransaction(boolean)
163165
*/
164166
@Unsafe
@@ -177,9 +179,11 @@ public interface TransactionSettings {
177179
*
178180
* @settingDefault {@code false} (disabled)
179181
*
180-
* @apiNote Generally speaking, all access to transactional data should be done in a transaction.
181-
* Combining this with second-level caching, e.g., will cause problems.
182+
* @apiNote Generally speaking, all access to transactional data should be
183+
* done in a transaction. Combining this with second-level caching
184+
* is not safe. Use of this setting is discouraged.
182185
*
186+
* @see org.hibernate.boot.spi.SessionFactoryOptions#isAllowOutOfTransactionUpdateOperations
183187
* @see org.hibernate.boot.SessionFactoryBuilder#allowOutOfTransactionUpdateOperations(boolean)
184188
*
185189
* @since 5.2

0 commit comments

Comments
 (0)