Polishing

This commit is contained in:
Juergen Hoeller 2023-08-06 14:25:39 +02:00
parent 4a81814dbb
commit 6e5af9dccb
13 changed files with 125 additions and 131 deletions

View File

@ -60,8 +60,8 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(ctx::refresh)
.withCauseInstanceOf(IllegalStateException.class);
.isThrownBy(ctx::refresh)
.withCauseInstanceOf(IllegalStateException.class);
}
@Test
@ -70,7 +70,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
Thread.sleep(200); // allow @Scheduled method to be called several times
MyRepository repository = ctx.getBean(MyRepository.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
@ -85,7 +85,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
Thread.sleep(200); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
@ -100,7 +100,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(AspectConfig.class, MyRepositoryWithScheduledMethodImpl.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
Thread.sleep(200); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
assertThat(AopUtils.isCglibProxy(repository)).isTrue();

View File

@ -77,9 +77,7 @@ public class ThrowsAdviceInterceptorTests {
given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode"));
given(mi.getThis()).willReturn(new Object());
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(ex);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("ioException")).isEqualTo(1);
}
@ -92,9 +90,7 @@ public class ThrowsAdviceInterceptorTests {
ConnectException ex = new ConnectException("");
MethodInvocation mi = mock();
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(ConnectException.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(ex);
assertThatExceptionOfType(ConnectException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("remoteException")).isEqualTo(1);
}
@ -117,9 +113,7 @@ public class ThrowsAdviceInterceptorTests {
ConnectException ex = new ConnectException("");
MethodInvocation mi = mock();
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(Throwable.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(t);
assertThatExceptionOfType(Throwable.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(t);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("remoteException")).isEqualTo(1);
}

View File

@ -114,7 +114,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -133,7 +133,7 @@ class QuartzSupportTests {
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
void jobDetailWithRunnableInsteadOfJob() {
JobDetailImpl jobDetail = new JobDetailImpl();
assertThatIllegalArgumentException().isThrownBy(() ->
@ -156,7 +156,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -190,7 +190,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -225,7 +225,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -260,7 +260,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();

View File

@ -329,8 +329,8 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* @return {@code true} if the bean can be registered as-is;
* {@code false} if it should be skipped because there is an
* existing, compatible bean definition for the specified name
* @throws ConflictingBeanDefinitionException if an existing, incompatible
* bean definition has been found for the specified name
* @throws IllegalStateException if an existing, incompatible bean definition
* has been found for the specified name
*/
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (!this.registry.containsBeanDefinition(beanName)) {
@ -354,16 +354,16 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* the given existing bean definition.
* <p>The default implementation considers them as compatible when the existing
* bean definition comes from the same source or from a non-scanning source.
* @param newDefinition the new bean definition, originated from scanning
* @param existingDefinition the existing bean definition, potentially an
* @param newDef the new bean definition, originated from scanning
* @param existingDef the existing bean definition, potentially an
* explicitly defined one or a previously generated one from scanning
* @return whether the definitions are considered as compatible, with the
* new definition to be skipped in favor of the existing definition
*/
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) || // scanned same file twice
newDefinition.equals(existingDefinition)); // scanned equivalent class twice
protected boolean isCompatible(BeanDefinition newDef, BeanDefinition existingDef) {
return (!(existingDef instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
(newDef.getSource() != null && newDef.getSource().equals(existingDef.getSource())) || // scanned same file twice
newDef.equals(existingDef)); // scanned equivalent class twice
}

View File

@ -197,6 +197,7 @@ public class ClassPathBeanDefinitionScannerTests {
context.registerBeanDefinition("stubFooDao", new RootBeanDefinition(TestBean.class));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
// should not fail!
scanner.scan(BASE_PACKAGE);
}
@ -207,6 +208,7 @@ public class ClassPathBeanDefinitionScannerTests {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.scan("org.springframework.context.annotation3");
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
.withMessageContaining("stubFooDao")
.withMessageContaining(StubFooDao.class.getName());

View File

@ -24,8 +24,6 @@ import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow;
import java.util.function.Function;
import kotlinx.coroutines.CompletableDeferredKt;
import kotlinx.coroutines.Deferred;
import org.reactivestreams.Publisher;
import reactor.adapter.JdkFlowAdapter;
import reactor.blockhound.BlockHound;
@ -38,13 +36,14 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
* A registry of adapters to adapt Reactive Streams {@link Publisher} to/from
* various async/reactive types such as {@code CompletableFuture}, RxJava
* {@code Flowable}, and others.
* A registry of adapters to adapt Reactive Streams {@link Publisher} to/from various
* async/reactive types such as {@code CompletableFuture}, RxJava {@code Flowable}, etc.
* This is designed to complement Spring's Reactor {@code Mono}/{@code Flux} support while
* also being usable without Reactor, e.g. just for {@code org.reactivestreams} bridging.
*
* <p>By default, depending on classpath availability, adapters are registered
* for Reactor, RxJava 3, {@link CompletableFuture}, {@code Flow.Publisher},
* and Kotlin Coroutines' {@code Deferred} and {@code Flow}.
* <p>By default, depending on classpath availability, adapters are registered for Reactor
* (including {@code CompletableFuture} and {@code Flow.Publisher} adapters), RxJava 3,
* Kotlin Coroutines' {@code Deferred} (bridged via Reactor) and SmallRye Mutiny 1.x.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
@ -304,9 +303,9 @@ public class ReactiveAdapterRegistry {
@SuppressWarnings("KotlinInternalInJava")
void registerAdapters(ReactiveAdapterRegistry registry) {
registry.registerReactiveType(
ReactiveTypeDescriptor.singleOptionalValue(Deferred.class,
() -> CompletableDeferredKt.CompletableDeferred(null)),
source -> CoroutinesUtils.deferredToMono((Deferred<?>) source),
ReactiveTypeDescriptor.singleOptionalValue(kotlinx.coroutines.Deferred.class,
() -> kotlinx.coroutines.CompletableDeferredKt.CompletableDeferred(null)),
source -> CoroutinesUtils.deferredToMono((kotlinx.coroutines.Deferred<?>) source),
source -> CoroutinesUtils.monoToDeferred(Mono.from(source)));
registry.registerReactiveType(

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,7 +37,7 @@ public final class ReactiveTypeDescriptor {
private final boolean noValue;
@Nullable
private final Supplier<?> emptyValueSupplier;
private final Supplier<?> emptySupplier;
private final boolean deferred;
@ -55,7 +55,7 @@ public final class ReactiveTypeDescriptor {
this.reactiveType = reactiveType;
this.multiValue = multiValue;
this.noValue = noValue;
this.emptyValueSupplier = emptySupplier;
this.emptySupplier = emptySupplier;
this.deferred = deferred;
}
@ -89,7 +89,7 @@ public final class ReactiveTypeDescriptor {
* Return {@code true} if the reactive type can complete with no values.
*/
public boolean supportsEmpty() {
return (this.emptyValueSupplier != null);
return (this.emptySupplier != null);
}
/**
@ -97,8 +97,8 @@ public final class ReactiveTypeDescriptor {
* <p>Use of this type implies {@link #supportsEmpty()} is {@code true}.
*/
public Object getEmptyValue() {
Assert.state(this.emptyValueSupplier != null, "Empty values not supported");
return this.emptyValueSupplier.get();
Assert.state(this.emptySupplier != null, "Empty values not supported");
return this.emptySupplier.get();
}
/**

View File

@ -20,6 +20,7 @@ import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Flow;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
@ -27,6 +28,7 @@ import kotlinx.coroutines.Deferred;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import reactor.adapter.JdkFlowAdapter;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -112,6 +114,16 @@ class ReactiveAdapterRegistryTests {
assertThat(((Mono<Integer>) target).block(ONE_SECOND)).isEqualTo(Integer.valueOf(1));
}
@Test
void toFlowPublisher() {
List<Integer> sequence = Arrays.asList(1, 2, 3);
Publisher<Integer> source = io.reactivex.rxjava3.core.Flowable.fromIterable(sequence);
Object target = getAdapter(Flow.Publisher.class).fromPublisher(source);
assertThat(target).isInstanceOf(Flow.Publisher.class);
assertThat(JdkFlowAdapter.flowPublisherToFlux((Flow.Publisher<Integer>) target)
.collectList().block(ONE_SECOND)).isEqualTo(sequence);
}
@Test
void toCompletableFuture() throws Exception {
Publisher<Integer> source = Flux.fromArray(new Integer[] {1, 2, 3});

View File

@ -88,8 +88,7 @@ class R2dbcTransactionManagerUnitTests {
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.flatMap(connection -> TransactionSynchronizationManager.forCurrentTransaction()
.doOnNext(synchronizationManager -> synchronizationManager.registerSynchronization(
sync)))
.doOnNext(synchronizationManager -> synchronizationManager.registerSynchronization(sync)))
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
@ -120,12 +119,11 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectErrorSatisfies(actual -> assertThat(actual).isInstanceOf(
CannotCreateTransactionException.class).hasCauseInstanceOf(
R2dbcBadGrammarException.class))
CannotCreateTransactionException.class).hasCauseInstanceOf(R2dbcBadGrammarException.class))
.verify();
}
@ -141,8 +139,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@ -171,8 +169,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@ -190,8 +188,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@ -215,8 +213,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@ -262,11 +260,9 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm);
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.doOnNext(connection -> {
throw new IllegalStateException();
}).as(operator::transactional)
.as(StepVerifier::create)
.verifyError(IllegalStateException.class);
.doOnNext(connection -> { throw new IllegalStateException(); })
.as(operator::transactional)
.as(StepVerifier::create).verifyError(IllegalStateException.class);
assertThat(commits).hasValue(0);
assertThat(rollbacks).hasValue(1);
@ -286,8 +282,7 @@ class R2dbcTransactionManagerUnitTests {
reactiveTransaction.setRollbackOnly();
return ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.doOnNext(connection -> connection.createStatement("foo")).then();
}).as(StepVerifier::create)
.verifyError(BadSqlGrammarException.class);
}).as(StepVerifier::create).verifyError(BadSqlGrammarException.class);
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
verify(connectionMock).createStatement("foo");
@ -308,7 +303,7 @@ class R2dbcTransactionManagerUnitTests {
.doOnNext(connection -> {
throw new IllegalStateException("Intentional error to trigger rollback");
}).then()).as(StepVerifier::create)
.verifyErrorSatisfies(e -> assertThat(e)
.verifyErrorSatisfies(ex -> assertThat(ex)
.isInstanceOf(BadSqlGrammarException.class)
.hasCause(new R2dbcBadGrammarException("Rollback should fail"))
);
@ -334,8 +329,7 @@ class R2dbcTransactionManagerUnitTests {
assertThat(synchronizationManager.hasResource(connectionFactoryMock)).isTrue();
synchronizationManager.registerSynchronization(sync);
}).then();
}).as(StepVerifier::create)
.verifyComplete();
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).beginTransaction(any(io.r2dbc.spi.TransactionDefinition.class));
verify(connectionMock).rollbackTransaction();
@ -363,8 +357,7 @@ class R2dbcTransactionManagerUnitTests {
fail("Should have thrown IllegalTransactionStateException");
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyError(IllegalTransactionStateException.class);
}).as(StepVerifier::create).verifyError(IllegalTransactionStateException.class);
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();
@ -381,14 +374,13 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isTrue();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
return operator.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.isNewTransaction()).isTrue();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
return operator.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).createSavepoint("SAVEPOINT_1");
verify(connectionMock).releaseSavepoint("SAVEPOINT_1");
@ -407,15 +399,14 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isTrue();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
return operator.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.isNewTransaction()).isTrue();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
return operator.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).createSavepoint("SAVEPOINT_1");
verify(connectionMock).rollbackTransactionToSavepoint("SAVEPOINT_1");
@ -432,16 +423,15 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).commitTransaction();
verify(connectionMock).close();
@ -456,17 +446,16 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();
@ -489,8 +478,7 @@ class R2dbcTransactionManagerUnitTests {
assertThat(tx2.isNewTransaction()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).commitTransaction();
verify(connectionMock).close();
@ -505,17 +493,16 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();

View File

@ -68,8 +68,7 @@ public interface PlatformTransactionManager extends TransactionManager {
* @see TransactionDefinition#getTimeout
* @see TransactionDefinition#isReadOnly
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
/**
* Commit the given transaction, with regard to its status. If the transaction

View File

@ -17,7 +17,7 @@
package org.springframework.transaction;
/**
* Representation of an ongoing reactive transaction.
* Representation of an ongoing {@link ReactiveTransactionManager} transaction.
* This is currently a marker interface extending {@link TransactionExecution}
* but may acquire further methods in a future revision.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,8 @@ package org.springframework.transaction;
import java.io.Flushable;
/**
* Representation of the status of a transaction.
* Representation of an ongoing {@link PlatformTransactionManager} transaction.
* Extends the common {@link TransactionExecution} interface.
*
* <p>Transactional code can use this to retrieve status information,
* and to programmatically request a rollback (instead of throwing

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,13 +23,13 @@ import java.io.Flushable;
* return an internal rollback-only marker, typically from another
* transaction that has participated and marked it as rollback-only.
*
* <p>Autodetected by DefaultTransactionStatus, to always return a
* current rollbackOnly flag even if not resulting from the current
* <p>Autodetected by {@link DefaultTransactionStatus} in order to always
* return a current rollbackOnly flag even if not resulting from the current
* TransactionStatus.
*
* @author Juergen Hoeller
* @since 1.1
* @see DefaultTransactionStatus#isRollbackOnly
* @see DefaultTransactionStatus#isGlobalRollbackOnly()
*/
public interface SmartTransactionObject extends Flushable {