Skip to content

Commit 4ac9f43

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 d104e7c commit 4ac9f43

File tree

13 files changed

+288
-172
lines changed

13 files changed

+288
-172
lines changed

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

Lines changed: 154 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,
@@ -44,31 +48,172 @@
4448
public interface Transaction extends EntityTransaction {
4549
/**
4650
* Get the current {@linkplain TransactionStatus status} of this transaction.
51+
*
52+
* @apiNote {@link TransactionStatus} belongs to an SPI package, and so this
53+
* operation is a (fairly harmless) layer-breaker. Prefer the use
54+
* of {@link #isActive}, {@link #isComplete}, {@link #wasStarted},
55+
* {@link #wasSuccessful}, {@link #wasFailure}, or
56+
* {@link #isInCompletionProcess} according to need.
57+
*
4758
*/
4859
TransactionStatus getStatus();
4960

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

59203
/**
60-
* Set the transaction timeout for any transaction started by any subsequent call to
61-
* {@link #begin} on this instance.
204+
* Set the transaction timeout for any transaction started by a subsequent call to
205+
* {@link #begin} on this instance of {@code Transaction}.
62206
*
63-
* @param seconds The number of seconds before a timeout.
207+
* @param seconds The number of seconds before a timeout
64208
*/
65209
void setTimeout(int seconds);
66210

67211
/**
68-
* Retrieve the transaction timeout set for this instance. A negative integer indicates
69-
* that no timeout has been set.
212+
* Retrieve the transaction timeout set for this instance.
213+
* <p>
214+
* A {@code null} return value indicates that no timeout has been set.
70215
*
71-
* @return The timeout, in seconds.
216+
* @return the timeout, in seconds, or {@code null}
72217
*/
73218
@Nullable Integer getTimeout();
74219

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)