Skip to content

Commit e5579c0

Browse files
committed
Enforce modifying methods are annotated with @transactional
Fix GH-3188
1 parent 35dc371 commit e5579c0

File tree

2 files changed

+24
-0
lines changed

2 files changed

+24
-0
lines changed

Diff for: spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java

+1
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ public boolean exists(Specification<T> spec) {
468468
}
469469

470470
@Override
471+
@Transactional
471472
public long delete(Specification<T> spec) {
472473

473474
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();

Diff for: spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/SimpleJpaRepositoryUnitTests.java

+23
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
package org.springframework.data.jpa.repository.support;
1717

1818
import static java.util.Collections.singletonMap;
19+
import static org.assertj.core.api.Assertions.assertThat;
1920
import static org.assertj.core.api.Assertions.assertThatNoException;
21+
import static org.assertj.core.api.Assertions.fail;
2022
import static org.mockito.Mockito.any;
2123
import static org.mockito.Mockito.never;
2224
import static org.mockito.Mockito.verify;
@@ -31,8 +33,11 @@
3133
import jakarta.persistence.criteria.CriteriaBuilder;
3234
import jakarta.persistence.criteria.CriteriaQuery;
3335

36+
import java.lang.reflect.Method;
37+
import java.lang.reflect.Modifier;
3438
import java.util.Arrays;
3539
import java.util.Optional;
40+
import java.util.stream.Stream;
3641

3742
import org.junit.jupiter.api.BeforeEach;
3843
import org.junit.jupiter.api.Test;
@@ -45,6 +50,7 @@
4550
import org.springframework.data.jpa.domain.sample.User;
4651
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
4752
import org.springframework.data.repository.CrudRepository;
53+
import org.springframework.transaction.annotation.Transactional;
4854

4955
/**
5056
* Unit tests for {@link SimpleJpaRepository}.
@@ -54,6 +60,7 @@
5460
* @author Mark Paluch
5561
* @author Jens Schauder
5662
* @author Greg Turnquist
63+
* @author Yanming Zhou
5764
*/
5865
@ExtendWith(MockitoExtension.class)
5966
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -216,4 +223,20 @@ void applyQueryHintsToCountQueriesForSpecificationPageables() {
216223

217224
verify(metadata).getQueryHintsForCount();
218225
}
226+
227+
@Test // GH-3188
228+
void checkTransactionalAnnotation() {
229+
Stream.of(SimpleJpaRepository.class.getDeclaredMethods()).filter(method -> Modifier.isPublic(method.getModifiers()) &&
230+
(method.getName().startsWith("delete") || method.getName().startsWith("save"))).forEach(
231+
method -> {
232+
if (!method.isAnnotationPresent(Transactional.class)) {
233+
fail("Method [" + method + "] should be annotated with @Transactional");
234+
}
235+
Transactional transactional = method.getAnnotation(Transactional.class);
236+
if (transactional.readOnly()) {
237+
fail("Method [" + method + "] should not be annotated with @Transactional(readOnly = true)");
238+
}
239+
}
240+
);
241+
}
219242
}

0 commit comments

Comments
 (0)