Merge branch '6.0.x'
# Conflicts: # spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java
This commit is contained in:
commit
a3e13c8ba8
|
|
@ -113,7 +113,8 @@ issue, because no compiler is involved, and you can declare
|
|||
When using `@Configuration` classes, the Java compiler places constraints on
|
||||
the configuration model, in that references to other beans must be valid Java syntax.
|
||||
|
||||
Fortunately, solving this problem is simple. As xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
|
||||
Fortunately, solving this problem is simple. As
|
||||
xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
|
||||
a `@Bean` method can have an arbitrary number of parameters that describe the bean
|
||||
dependencies. Consider the following more real-world scenario with several `@Configuration`
|
||||
classes, each depending on beans declared in the others:
|
||||
|
|
@ -204,7 +205,6 @@ Kotlin::
|
|||
----
|
||||
======
|
||||
|
||||
|
||||
There is another way to achieve the same result. Remember that `@Configuration` classes are
|
||||
ultimately only another bean in the container: This means that they can take advantage of
|
||||
`@Autowired` and `@Value` injection and other features the same as any other bean.
|
||||
|
|
@ -216,6 +216,11 @@ classes are processed quite early during the initialization of the context, and
|
|||
to be injected this way may lead to unexpected early initialization. Whenever possible, resort to
|
||||
parameter-based injection, as in the preceding example.
|
||||
|
||||
Avoid access to locally defined beans within a `@PostConstruct` method on the same configuration
|
||||
class. This effectively leads to a circular reference since non-static `@Bean` methods semantically
|
||||
require a fully initialized configuration class instance to be called on. With circular references
|
||||
disallowed (e.g. in Spring Boot 2.6+), this may trigger a `BeanCurrentlyInCreationException`.
|
||||
|
||||
Also, be particularly careful with `BeanPostProcessor` and `BeanFactoryPostProcessor` definitions
|
||||
through `@Bean`. Those should usually be declared as `static @Bean` methods, not triggering the
|
||||
instantiation of their containing configuration class. Otherwise, `@Autowired` and `@Value` may not
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ import org.springframework.stereotype.Component;
|
|||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using
|
||||
* component scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using component
|
||||
* scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* the {@link ComponentScan @ComponentScan} annotation:
|
||||
*
|
||||
* <pre class="code">
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ import org.springframework.util.Assert;
|
|||
|
||||
/**
|
||||
* Implementation of {@link BackOff} that increases the back off period for each
|
||||
* retry attempt. When the interval has reached the {@linkplain #setMaxInterval(long)
|
||||
* retry attempt. When the interval has reached the {@linkplain #setMaxInterval
|
||||
* max interval}, it is no longer increased. Stops retrying once the
|
||||
* {@linkplain #setMaxElapsedTime(long) max elapsed time} has been reached.
|
||||
* {@linkplain #setMaxElapsedTime max elapsed time} has been reached.
|
||||
*
|
||||
* <p>Example: The default interval is {@value #DEFAULT_INITIAL_INTERVAL} ms;
|
||||
* the default multiplier is {@value #DEFAULT_MULTIPLIER}; and the default max
|
||||
|
|
@ -45,10 +45,10 @@ import org.springframework.util.Assert;
|
|||
* </pre>
|
||||
*
|
||||
* <p>Note that the default max elapsed time is {@link Long#MAX_VALUE}, and the
|
||||
* default maximum number of attempts is {@link Integer#MAX_VALUE}. Use
|
||||
* {@link #setMaxElapsedTime(long)} to limit the length of time that an instance
|
||||
* default maximum number of attempts is {@link Integer#MAX_VALUE}.
|
||||
* Use {@link #setMaxElapsedTime} to limit the length of time that an instance
|
||||
* should accumulate before returning {@link BackOffExecution#STOP}. Alternatively,
|
||||
* use {@link #setMaxAttempts(int)} to limit the number of attempts. The execution
|
||||
* use {@link #setMaxAttempts} to limit the number of attempts. The execution
|
||||
* stops when either of those two limits is reached.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
|
|
@ -117,7 +117,7 @@ public class ExponentialBackOff implements BackOff {
|
|||
|
||||
|
||||
/**
|
||||
* The initial interval in milliseconds.
|
||||
* Set the initial interval in milliseconds.
|
||||
*/
|
||||
public void setInitialInterval(long initialInterval) {
|
||||
this.initialInterval = initialInterval;
|
||||
|
|
@ -131,7 +131,7 @@ public class ExponentialBackOff implements BackOff {
|
|||
}
|
||||
|
||||
/**
|
||||
* The value to multiply the current interval by for each retry attempt.
|
||||
* Set the value to multiply the current interval by for each retry attempt.
|
||||
*/
|
||||
public void setMultiplier(double multiplier) {
|
||||
checkMultiplier(multiplier);
|
||||
|
|
@ -146,24 +146,24 @@ public class ExponentialBackOff implements BackOff {
|
|||
}
|
||||
|
||||
/**
|
||||
* The maximum back off time.
|
||||
* Set the maximum back off time in milliseconds.
|
||||
*/
|
||||
public void setMaxInterval(long maxInterval) {
|
||||
this.maxInterval = maxInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum back off time.
|
||||
* Return the maximum back off time in milliseconds.
|
||||
*/
|
||||
public long getMaxInterval() {
|
||||
return this.maxInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum elapsed time in milliseconds after which a call to
|
||||
* Set the maximum elapsed time in milliseconds after which a call to
|
||||
* {@link BackOffExecution#nextBackOff()} returns {@link BackOffExecution#STOP}.
|
||||
* @param maxElapsedTime the maximum elapsed time
|
||||
* @see #setMaxAttempts(int)
|
||||
* @see #setMaxAttempts
|
||||
*/
|
||||
public void setMaxElapsedTime(long maxElapsedTime) {
|
||||
this.maxElapsedTime = maxElapsedTime;
|
||||
|
|
@ -184,7 +184,7 @@ public class ExponentialBackOff implements BackOff {
|
|||
* {@link BackOffExecution#nextBackOff()} returns {@link BackOffExecution#STOP}.
|
||||
* @param maxAttempts the maximum number of attempts
|
||||
* @since 6.1
|
||||
* @see #setMaxElapsedTime(long)
|
||||
* @see #setMaxElapsedTime
|
||||
*/
|
||||
public void setMaxAttempts(int maxAttempts) {
|
||||
this.maxAttempts = maxAttempts;
|
||||
|
|
@ -222,11 +222,9 @@ public class ExponentialBackOff implements BackOff {
|
|||
|
||||
@Override
|
||||
public long nextBackOff() {
|
||||
if (this.currentElapsedTime >= getMaxElapsedTime()
|
||||
|| this.attempts >= getMaxAttempts()) {
|
||||
if (this.currentElapsedTime >= getMaxElapsedTime() || this.attempts >= getMaxAttempts()) {
|
||||
return STOP;
|
||||
}
|
||||
|
||||
long nextInterval = computeNextInterval();
|
||||
this.currentElapsedTime += nextInterval;
|
||||
this.attempts++;
|
||||
|
|
@ -254,7 +252,6 @@ public class ExponentialBackOff implements BackOff {
|
|||
return Math.min(i, maxInterval);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("ExponentialBackOff{");
|
||||
|
|
|
|||
|
|
@ -252,24 +252,24 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
|
|||
if (ex instanceof JDBCConnectionException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof SQLGrammarException hibJdbcEx) {
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
|
||||
if (ex instanceof SQLGrammarException hibEx) {
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
|
||||
}
|
||||
if (ex instanceof QueryTimeoutException hibJdbcEx) {
|
||||
return new org.springframework.dao.QueryTimeoutException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
|
||||
if (ex instanceof QueryTimeoutException hibEx) {
|
||||
return new org.springframework.dao.QueryTimeoutException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
|
||||
}
|
||||
if (ex instanceof LockAcquisitionException hibJdbcEx) {
|
||||
return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
|
||||
if (ex instanceof LockAcquisitionException hibEx) {
|
||||
return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
|
||||
}
|
||||
if (ex instanceof PessimisticLockException hibJdbcEx) {
|
||||
return new PessimisticLockingFailureException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
|
||||
if (ex instanceof PessimisticLockException hibEx) {
|
||||
return new PessimisticLockingFailureException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
|
||||
}
|
||||
if (ex instanceof ConstraintViolationException hibJdbcEx) {
|
||||
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() +
|
||||
"]; constraint [" + hibJdbcEx.getConstraintName() + "]", ex);
|
||||
if (ex instanceof ConstraintViolationException hibEx) {
|
||||
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibEx.getSQL() +
|
||||
"]; constraint [" + hibEx.getConstraintName() + "]", ex);
|
||||
}
|
||||
if (ex instanceof DataException hibJdbcEx) {
|
||||
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibJdbcEx.getSQL() + "]", ex);
|
||||
if (ex instanceof DataException hibEx) {
|
||||
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + hibEx.getSQL() + "]", ex);
|
||||
}
|
||||
// end of JDBCException subclass handling
|
||||
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
connectionMono = Mono.just(txObject.getConnectionHolder().getConnection());
|
||||
}
|
||||
|
||||
return connectionMono.flatMap(con -> doBegin(definition, con)
|
||||
return connectionMono.flatMap(con -> doBegin(con, txObject, definition)
|
||||
.then(prepareTransactionalConnection(con, definition))
|
||||
.doOnSuccess(v -> {
|
||||
txObject.getConnectionHolder().setTransactionActive(true);
|
||||
|
|
@ -233,7 +233,10 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
}).then();
|
||||
}
|
||||
|
||||
private Mono<Void> doBegin(TransactionDefinition definition, Connection con) {
|
||||
private Mono<Void> doBegin(
|
||||
Connection con, ConnectionFactoryTransactionObject transaction, TransactionDefinition definition) {
|
||||
|
||||
transaction.setMustRestoreAutoCommit(con.isAutoCommit());
|
||||
io.r2dbc.spi.TransactionDefinition transactionDefinition = createTransactionDefinition(definition);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Starting R2DBC transaction on Connection [" + con + "] using [" + transactionDefinition + "]");
|
||||
|
|
@ -354,12 +357,22 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Releasing R2DBC Connection [" + con + "] after transaction");
|
||||
}
|
||||
Mono<Void> restoreMono = Mono.empty();
|
||||
if (txObject.isMustRestoreAutoCommit() && !con.isAutoCommit()) {
|
||||
restoreMono = Mono.from(con.setAutoCommit(true));
|
||||
if (logger.isDebugEnabled()) {
|
||||
restoreMono = restoreMono.doOnError(ex ->
|
||||
logger.debug(String.format("Error ignored during auto-commit restore: %s", ex)));
|
||||
}
|
||||
restoreMono = restoreMono.onErrorComplete();
|
||||
}
|
||||
Mono<Void> releaseMono = ConnectionFactoryUtils.releaseConnection(con, obtainConnectionFactory());
|
||||
if (logger.isDebugEnabled()) {
|
||||
releaseMono = releaseMono.doOnError(
|
||||
ex -> logger.debug(String.format("Error ignored during cleanup: %s", ex)));
|
||||
releaseMono = releaseMono.doOnError(ex ->
|
||||
logger.debug(String.format("Error ignored during connection release: %s", ex)));
|
||||
}
|
||||
return releaseMono.onErrorComplete();
|
||||
releaseMono = releaseMono.onErrorComplete();
|
||||
return restoreMono.then(releaseMono);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
|
|
@ -482,6 +495,8 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
|
||||
private boolean newConnectionHolder;
|
||||
|
||||
private boolean mustRestoreAutoCommit;
|
||||
|
||||
@Nullable
|
||||
private String savepointName;
|
||||
|
||||
|
|
@ -507,6 +522,14 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
|||
return (this.connectionHolder != null);
|
||||
}
|
||||
|
||||
public void setMustRestoreAutoCommit(boolean mustRestoreAutoCommit) {
|
||||
this.mustRestoreAutoCommit = mustRestoreAutoCommit;
|
||||
}
|
||||
|
||||
public boolean isMustRestoreAutoCommit() {
|
||||
return this.mustRestoreAutoCommit;
|
||||
}
|
||||
|
||||
public boolean isTransactionActive() {
|
||||
return (this.connectionHolder != null && this.connectionHolder.isTransactionActive());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,9 +170,8 @@ final class DefaultDatabaseClient implements DatabaseClient {
|
|||
* @return a {@link Publisher} that completes successfully when the connection is closed
|
||||
*/
|
||||
private Publisher<Void> closeConnection(Connection connection) {
|
||||
return ConnectionFactoryUtils.currentConnectionFactory(
|
||||
obtainConnectionFactory()).then().onErrorResume(Exception.class,
|
||||
e -> Mono.from(connection.close()));
|
||||
return ConnectionFactoryUtils.currentConnectionFactory(obtainConnectionFactory()).then()
|
||||
.onErrorResume(Exception.class, ex -> Mono.from(connection.close()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -81,11 +81,12 @@ class R2dbcTransactionManagerUnitTests {
|
|||
|
||||
@Test
|
||||
void testSimpleTransaction() {
|
||||
TestTransactionSynchronization sync = new TestTransactionSynchronization(
|
||||
TransactionSynchronization.STATUS_COMMITTED);
|
||||
when(connectionMock.isAutoCommit()).thenReturn(false);
|
||||
AtomicInteger commits = new AtomicInteger();
|
||||
when(connectionMock.commitTransaction()).thenReturn(
|
||||
Mono.fromRunnable(commits::incrementAndGet));
|
||||
TestTransactionSynchronization sync = new TestTransactionSynchronization(
|
||||
TransactionSynchronization.STATUS_COMMITTED);
|
||||
|
||||
TransactionalOperator operator = TransactionalOperator.create(tm);
|
||||
|
||||
|
|
@ -98,6 +99,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
.verifyComplete();
|
||||
|
||||
assertThat(commits).hasValue(1);
|
||||
verify(connectionMock).isAutoCommit();
|
||||
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||
verify(connectionMock).commitTransaction();
|
||||
verify(connectionMock).close();
|
||||
|
|
@ -131,8 +133,10 @@ class R2dbcTransactionManagerUnitTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void appliesTransactionDefinition() {
|
||||
void appliesTransactionDefinitionAndAutoCommit() {
|
||||
when(connectionMock.isAutoCommit()).thenReturn(true, false);
|
||||
when(connectionMock.commitTransaction()).thenReturn(Mono.empty());
|
||||
when(connectionMock.setAutoCommit(true)).thenReturn(Mono.empty());
|
||||
|
||||
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
|
||||
definition.setName("my-transaction");
|
||||
|
|
@ -156,6 +160,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
verify(connectionMock).beginTransaction(txCaptor.capture());
|
||||
verify(connectionMock, never()).setTransactionIsolationLevel(any());
|
||||
verify(connectionMock).commitTransaction();
|
||||
verify(connectionMock).setAutoCommit(true);
|
||||
verify(connectionMock).close();
|
||||
|
||||
io.r2dbc.spi.TransactionDefinition def = txCaptor.getValue();
|
||||
|
|
@ -166,29 +171,8 @@ class R2dbcTransactionManagerUnitTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void doesNotSetIsolationLevelIfMatch() {
|
||||
when(connectionMock.getTransactionIsolationLevel()).thenReturn(
|
||||
IsolationLevel.READ_COMMITTED);
|
||||
when(connectionMock.commitTransaction()).thenReturn(Mono.empty());
|
||||
|
||||
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
|
||||
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
|
||||
|
||||
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
|
||||
|
||||
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
|
||||
.as(operator::transactional)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||
verify(connectionMock, never()).setTransactionIsolationLevel(any());
|
||||
verify(connectionMock).commitTransaction();
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotSetAutoCommitDisabled() {
|
||||
void doesNotSetAutoCommitIfRestoredByDriver() {
|
||||
when(connectionMock.isAutoCommit()).thenReturn(true, true);
|
||||
when(connectionMock.commitTransaction()).thenReturn(Mono.empty());
|
||||
|
||||
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
|
||||
|
|
@ -208,6 +192,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
|
||||
@Test
|
||||
void appliesReadOnly() {
|
||||
when(connectionMock.isAutoCommit()).thenReturn(false);
|
||||
when(connectionMock.commitTransaction()).thenReturn(Mono.empty());
|
||||
when(connectionMock.setTransactionIsolationLevel(any())).thenReturn(Mono.empty());
|
||||
Statement statement = mock();
|
||||
|
|
@ -226,6 +211,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
verify(connectionMock).isAutoCommit();
|
||||
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||
verify(connectionMock).createStatement("SET TRANSACTION READ ONLY");
|
||||
verify(connectionMock).commitTransaction();
|
||||
|
|
@ -235,7 +221,9 @@ class R2dbcTransactionManagerUnitTests {
|
|||
|
||||
@Test
|
||||
void testCommitFails() {
|
||||
when(connectionMock.commitTransaction()).thenReturn(Mono.defer(() -> Mono.error(new R2dbcBadGrammarException("Commit should fail"))));
|
||||
when(connectionMock.isAutoCommit()).thenReturn(false);
|
||||
when(connectionMock.commitTransaction()).thenReturn(Mono.defer(() ->
|
||||
Mono.error(new R2dbcBadGrammarException("Commit should fail"))));
|
||||
when(connectionMock.rollbackTransaction()).thenReturn(Mono.empty());
|
||||
|
||||
TransactionalOperator operator = TransactionalOperator.create(tm);
|
||||
|
|
@ -246,6 +234,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
.as(StepVerifier::create)
|
||||
.verifyError(BadSqlGrammarException.class);
|
||||
|
||||
verify(connectionMock).isAutoCommit();
|
||||
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||
verify(connectionMock).createStatement("foo");
|
||||
verify(connectionMock).commitTransaction();
|
||||
|
|
@ -256,6 +245,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
|
||||
@Test
|
||||
void testRollback() {
|
||||
when(connectionMock.isAutoCommit()).thenReturn(false);
|
||||
AtomicInteger commits = new AtomicInteger();
|
||||
when(connectionMock.commitTransaction()).thenReturn(
|
||||
Mono.fromRunnable(commits::incrementAndGet));
|
||||
|
|
@ -273,6 +263,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
|
||||
assertThat(commits).hasValue(0);
|
||||
assertThat(rollbacks).hasValue(1);
|
||||
verify(connectionMock).isAutoCommit();
|
||||
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||
verify(connectionMock).rollbackTransaction();
|
||||
verify(connectionMock).close();
|
||||
|
|
@ -282,7 +273,8 @@ class R2dbcTransactionManagerUnitTests {
|
|||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void testRollbackFails() {
|
||||
when(connectionMock.rollbackTransaction()).thenReturn(Mono.defer(() -> Mono.error(new R2dbcBadGrammarException("Commit should fail"))), Mono.empty());
|
||||
when(connectionMock.rollbackTransaction()).thenReturn(Mono.defer(() ->
|
||||
Mono.error(new R2dbcBadGrammarException("Commit should fail"))), Mono.empty());
|
||||
|
||||
TransactionalOperator operator = TransactionalOperator.create(tm);
|
||||
operator.execute(reactiveTransaction -> {
|
||||
|
|
@ -291,6 +283,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
.doOnNext(connection -> connection.createStatement("foo")).then();
|
||||
}).as(StepVerifier::create).verifyError(BadSqlGrammarException.class);
|
||||
|
||||
verify(connectionMock).isAutoCommit();
|
||||
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||
verify(connectionMock).createStatement("foo");
|
||||
verify(connectionMock, never()).commitTransaction();
|
||||
|
|
@ -302,7 +295,8 @@ class R2dbcTransactionManagerUnitTests {
|
|||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void testConnectionReleasedWhenRollbackFails() {
|
||||
when(connectionMock.rollbackTransaction()).thenReturn(Mono.defer(() -> Mono.error(new R2dbcBadGrammarException("Rollback should fail"))), Mono.empty());
|
||||
when(connectionMock.rollbackTransaction()).thenReturn(Mono.defer(() ->
|
||||
Mono.error(new R2dbcBadGrammarException("Rollback should fail"))), Mono.empty());
|
||||
when(connectionMock.setTransactionIsolationLevel(any())).thenReturn(Mono.empty());
|
||||
|
||||
TransactionalOperator operator = TransactionalOperator.create(tm);
|
||||
|
|
@ -323,6 +317,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
|
||||
@Test
|
||||
void testTransactionSetRollbackOnly() {
|
||||
when(connectionMock.isAutoCommit()).thenReturn(false);
|
||||
when(connectionMock.rollbackTransaction()).thenReturn(Mono.empty());
|
||||
TestTransactionSynchronization sync = new TestTransactionSynchronization(
|
||||
TransactionSynchronization.STATUS_ROLLED_BACK);
|
||||
|
|
@ -345,6 +340,7 @@ class R2dbcTransactionManagerUnitTests {
|
|||
}).then();
|
||||
}).as(StepVerifier::create).verifyComplete();
|
||||
|
||||
verify(connectionMock).isAutoCommit();
|
||||
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
|
||||
verify(connectionMock).rollbackTransaction();
|
||||
verify(connectionMock).close();
|
||||
|
|
|
|||
Loading…
Reference in New Issue