diff --git a/spring-session-docs/src/test/java/docs/IndexDocTests.java b/spring-session-docs/src/test/java/docs/IndexDocTests.java index 3471535d..3e61f605 100644 --- a/spring-session-docs/src/test/java/docs/IndexDocTests.java +++ b/spring-session-docs/src/test/java/docs/IndexDocTests.java @@ -30,7 +30,6 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.mock.web.MockServletContext; import org.springframework.session.MapSession; import org.springframework.session.MapSessionRepository; @@ -42,7 +41,7 @@ import org.springframework.session.data.redis.RedisOperationsSessionRepository; import org.springframework.session.hazelcast.HazelcastSessionRepository; import org.springframework.session.jdbc.JdbcOperationsSessionRepository; import org.springframework.session.web.http.SessionRepositoryFilter; -import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.support.TransactionTemplate; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -162,14 +161,14 @@ class IndexDocTests { // tag::new-jdbcoperationssessionrepository[] JdbcTemplate jdbcTemplate = new JdbcTemplate(); - // ... configure JdbcTemplate ... + // ... configure jdbcTemplate ... - PlatformTransactionManager transactionManager = new DataSourceTransactionManager(); + TransactionTemplate transactionTemplate = new TransactionTemplate(); - // ... configure transactionManager ... + // ... configure transactionTemplate ... SessionRepository repository = new JdbcOperationsSessionRepository(jdbcTemplate, - transactionManager); + transactionTemplate); // end::new-jdbcoperationssessionrepository[] } diff --git a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java index 23fe757d..11a4bbe4 100644 --- a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java +++ b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java @@ -42,7 +42,6 @@ import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcOperations; -import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.support.lob.DefaultLobHandler; import org.springframework.jdbc.support.lob.LobHandler; @@ -56,8 +55,6 @@ import org.springframework.session.SaveMode; import org.springframework.session.Session; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionOperations; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.util.Assert; @@ -75,16 +72,16 @@ import org.springframework.util.StringUtils; * * // ... configure jdbcTemplate ... * - * PlatformTransactionManager transactionManager = new DataSourceTransactionManager(); + * TransactionTemplate transactionTemplate = new TransactionTemplate(); * - * // ... configure transactionManager ... + * // ... configure transactionTemplate ... * * JdbcOperationsSessionRepository sessionRepository = - * new JdbcOperationsSessionRepository(jdbcTemplate, transactionManager); + * new JdbcOperationsSessionRepository(jdbcTemplate, transactionTemplate); * * - * For additional information on how to create and configure {@link JdbcTemplate} and - * {@link PlatformTransactionManager}, refer to the * Spring Framework Reference Documentation. *

@@ -200,12 +197,12 @@ public class JdbcOperationsSessionRepository private final JdbcOperations jdbcOperations; + private final TransactionOperations transactionOperations; + private final ResultSetExtractor> extractor = new SessionResultSetExtractor(); private final IndexResolver indexResolver; - private TransactionOperations transactionOperations = TransactionOperations.withoutTransaction(); - /** * The name of database table used by Spring Session to store sessions. */ @@ -237,12 +234,30 @@ public class JdbcOperationsSessionRepository private ConversionService conversionService; - private LobHandler lobHandler = new DefaultLobHandler(); + private LobHandler lobHandler; private FlushMode flushMode = FlushMode.ON_SAVE; private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE; + /** + * Create a new {@link JdbcOperationsSessionRepository} instance which uses the + * provided {@link JdbcOperations} and {@link TransactionOperations} to manage + * sessions. + * @param jdbcOperations the {@link JdbcOperations} to use + * @param transactionOperations the {@link TransactionOperations} to use + */ + public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations, TransactionOperations transactionOperations) { + Assert.notNull(jdbcOperations, "jdbcOperations must not be null"); + Assert.notNull(transactionOperations, "transactionOperations must not be null"); + this.jdbcOperations = jdbcOperations; + this.transactionOperations = transactionOperations; + this.indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>()); + this.conversionService = createDefaultConversionService(); + this.lobHandler = new DefaultLobHandler(); + prepareQueries(); + } + /** * Create a new {@link JdbcOperationsSessionRepository} instance which uses the * provided {@link JdbcOperations} to manage sessions. @@ -251,12 +266,13 @@ public class JdbcOperationsSessionRepository * propagation level of {@link TransactionDefinition#PROPAGATION_REQUIRES_NEW}. * @param jdbcOperations the {@link JdbcOperations} to use * @param transactionManager the {@link PlatformTransactionManager} to use + * @deprecated since 2.2.0 in favor of + * {@link #JdbcOperationsSessionRepository(JdbcOperations, TransactionOperations)} */ + @Deprecated public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations, PlatformTransactionManager transactionManager) { - this(jdbcOperations); - Assert.notNull(transactionManager, "TransactionManager must not be null"); - this.transactionOperations = createTransactionTemplate(transactionManager); + this(jdbcOperations, createTransactionTemplate(transactionManager)); } /** @@ -265,13 +281,12 @@ public class JdbcOperationsSessionRepository *

* The created instance will not execute data access operations in a transaction. * @param jdbcOperations the {@link JdbcOperations} to use + * @deprecated since 2.2.0 in favor of + * {@link #JdbcOperationsSessionRepository(JdbcOperations, TransactionOperations)} */ + @Deprecated public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations) { - Assert.notNull(jdbcOperations, "JdbcOperations must not be null"); - this.jdbcOperations = jdbcOperations; - this.indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>()); - this.conversionService = createDefaultConversionService(); - prepareQueries(); + this(jdbcOperations, TransactionOperations.withoutTransaction()); } /** @@ -448,15 +463,8 @@ public class JdbcOperationsSessionRepository @Override public void deleteById(final String id) { - this.transactionOperations.execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - JdbcOperationsSessionRepository.this.jdbcOperations - .update(JdbcOperationsSessionRepository.this.deleteSessionQuery, id); - } - - }); + this.transactionOperations.execute(() -> JdbcOperationsSessionRepository.this.jdbcOperations + .update(JdbcOperationsSessionRepository.this.deleteSessionQuery, id)); } @Override @@ -581,6 +589,7 @@ public class JdbcOperationsSessionRepository } private static TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) { + Assert.notNull(transactionManager, "transactionManager must not be null"); TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); transactionTemplate.afterPropertiesSet(); @@ -805,71 +814,59 @@ public class JdbcOperationsSessionRepository private void save() { if (this.isNew) { - JdbcOperationsSessionRepository.this.transactionOperations - .execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - Map indexes = JdbcOperationsSessionRepository.this.indexResolver - .resolveIndexesFor(JdbcSession.this); - JdbcOperationsSessionRepository.this.jdbcOperations - .update(JdbcOperationsSessionRepository.this.createSessionQuery, (ps) -> { - ps.setString(1, JdbcSession.this.primaryKey); - ps.setString(2, getId()); - ps.setLong(3, getCreationTime().toEpochMilli()); - ps.setLong(4, getLastAccessedTime().toEpochMilli()); - ps.setInt(5, (int) getMaxInactiveInterval().getSeconds()); - ps.setLong(6, getExpiryTime().toEpochMilli()); - ps.setString(7, indexes.get(PRINCIPAL_NAME_INDEX_NAME)); - }); - Set attributeNames = getAttributeNames(); - if (!attributeNames.isEmpty()) { - insertSessionAttributes(JdbcSession.this, new ArrayList<>(attributeNames)); - } - } - - }); + JdbcOperationsSessionRepository.this.transactionOperations.execute(() -> { + Map indexes = JdbcOperationsSessionRepository.this.indexResolver + .resolveIndexesFor(JdbcSession.this); + JdbcOperationsSessionRepository.this.jdbcOperations + .update(JdbcOperationsSessionRepository.this.createSessionQuery, (ps) -> { + ps.setString(1, JdbcSession.this.primaryKey); + ps.setString(2, getId()); + ps.setLong(3, getCreationTime().toEpochMilli()); + ps.setLong(4, getLastAccessedTime().toEpochMilli()); + ps.setInt(5, (int) getMaxInactiveInterval().getSeconds()); + ps.setLong(6, getExpiryTime().toEpochMilli()); + ps.setString(7, indexes.get(PRINCIPAL_NAME_INDEX_NAME)); + }); + Set attributeNames = getAttributeNames(); + if (!attributeNames.isEmpty()) { + insertSessionAttributes(JdbcSession.this, new ArrayList<>(attributeNames)); + } + }); } else { - JdbcOperationsSessionRepository.this.transactionOperations - .execute(new TransactionCallbackWithoutResult() { - - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - if (JdbcSession.this.changed) { - Map indexes = JdbcOperationsSessionRepository.this.indexResolver - .resolveIndexesFor(JdbcSession.this); - JdbcOperationsSessionRepository.this.jdbcOperations - .update(JdbcOperationsSessionRepository.this.updateSessionQuery, (ps) -> { - ps.setString(1, getId()); - ps.setLong(2, getLastAccessedTime().toEpochMilli()); - ps.setInt(3, (int) getMaxInactiveInterval().getSeconds()); - ps.setLong(4, getExpiryTime().toEpochMilli()); - ps.setString(5, indexes.get(PRINCIPAL_NAME_INDEX_NAME)); - ps.setString(6, JdbcSession.this.primaryKey); - }); - } - List addedAttributeNames = JdbcSession.this.delta.entrySet().stream() - .filter((entry) -> entry.getValue() == DeltaValue.ADDED).map(Map.Entry::getKey) - .collect(Collectors.toList()); - if (!addedAttributeNames.isEmpty()) { - insertSessionAttributes(JdbcSession.this, addedAttributeNames); - } - List updatedAttributeNames = JdbcSession.this.delta.entrySet().stream() - .filter((entry) -> entry.getValue() == DeltaValue.UPDATED) - .map(Map.Entry::getKey).collect(Collectors.toList()); - if (!updatedAttributeNames.isEmpty()) { - updateSessionAttributes(JdbcSession.this, updatedAttributeNames); - } - List removedAttributeNames = JdbcSession.this.delta.entrySet().stream() - .filter((entry) -> entry.getValue() == DeltaValue.REMOVED) - .map(Map.Entry::getKey).collect(Collectors.toList()); - if (!removedAttributeNames.isEmpty()) { - deleteSessionAttributes(JdbcSession.this, removedAttributeNames); - } - } - - }); + JdbcOperationsSessionRepository.this.transactionOperations.execute(() -> { + if (JdbcSession.this.changed) { + Map indexes = JdbcOperationsSessionRepository.this.indexResolver + .resolveIndexesFor(JdbcSession.this); + JdbcOperationsSessionRepository.this.jdbcOperations + .update(JdbcOperationsSessionRepository.this.updateSessionQuery, (ps) -> { + ps.setString(1, getId()); + ps.setLong(2, getLastAccessedTime().toEpochMilli()); + ps.setInt(3, (int) getMaxInactiveInterval().getSeconds()); + ps.setLong(4, getExpiryTime().toEpochMilli()); + ps.setString(5, indexes.get(PRINCIPAL_NAME_INDEX_NAME)); + ps.setString(6, JdbcSession.this.primaryKey); + }); + } + List addedAttributeNames = JdbcSession.this.delta.entrySet().stream() + .filter((entry) -> entry.getValue() == DeltaValue.ADDED).map(Map.Entry::getKey) + .collect(Collectors.toList()); + if (!addedAttributeNames.isEmpty()) { + insertSessionAttributes(JdbcSession.this, addedAttributeNames); + } + List updatedAttributeNames = JdbcSession.this.delta.entrySet().stream() + .filter((entry) -> entry.getValue() == DeltaValue.UPDATED).map(Map.Entry::getKey) + .collect(Collectors.toList()); + if (!updatedAttributeNames.isEmpty()) { + updateSessionAttributes(JdbcSession.this, updatedAttributeNames); + } + List removedAttributeNames = JdbcSession.this.delta.entrySet().stream() + .filter((entry) -> entry.getValue() == DeltaValue.REMOVED).map(Map.Entry::getKey) + .collect(Collectors.toList()); + if (!removedAttributeNames.isEmpty()) { + deleteSessionAttributes(JdbcSession.this, removedAttributeNames); + } + }); } clearChangeFlags(); } diff --git a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java index 2355e052..33c7d30b 100644 --- a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java +++ b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java @@ -50,6 +50,9 @@ import org.springframework.session.jdbc.JdbcOperationsSessionRepository; import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource; import org.springframework.session.web.http.SessionRepositoryFilter; import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.TransactionOperations; +import org.springframework.transaction.support.TransactionTemplate; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; @@ -87,6 +90,8 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration private PlatformTransactionManager transactionManager; + private TransactionOperations transactionOperations; + private LobHandler lobHandler; private ConversionService springSessionConversionService; @@ -100,8 +105,11 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration @Bean public JdbcOperationsSessionRepository sessionRepository() { JdbcTemplate jdbcTemplate = createJdbcTemplate(this.dataSource); + if (this.transactionOperations == null) { + this.transactionOperations = createTransactionTemplate(this.transactionManager); + } JdbcOperationsSessionRepository sessionRepository = new JdbcOperationsSessionRepository(jdbcTemplate, - this.transactionManager); + this.transactionOperations); if (StringUtils.hasText(this.tableName)) { sessionRepository.setTableName(this.tableName); } @@ -123,7 +131,7 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration sessionRepository.setConversionService(this.conversionService); } else { - sessionRepository.setConversionService(createConversionServiceWithBeanClassLoader()); + sessionRepository.setConversionService(createConversionServiceWithBeanClassLoader(this.classLoader)); } return sessionRepository; } @@ -173,6 +181,12 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration this.transactionManager = transactionManager; } + @Autowired(required = false) + @Qualifier("springSessionTransactionOperations") + public void setTransactionOperations(TransactionOperations transactionOperations) { + this.transactionOperations = transactionOperations; + } + @Autowired(required = false) @Qualifier("springSessionLobHandler") public void setLobHandler(LobHandler lobHandler) { @@ -230,10 +244,17 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration return jdbcTemplate; } - private GenericConversionService createConversionServiceWithBeanClassLoader() { + private static TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) { + TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + transactionTemplate.afterPropertiesSet(); + return transactionTemplate; + } + + private static GenericConversionService createConversionServiceWithBeanClassLoader(ClassLoader classLoader) { GenericConversionService conversionService = new GenericConversionService(); conversionService.addConverter(Object.class, byte[].class, new SerializingConverter()); - conversionService.addConverter(byte[].class, Object.class, new DeserializingConverter(this.classLoader)); + conversionService.addConverter(byte[].class, Object.class, new DeserializingConverter(classLoader)); return conversionService; } diff --git a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcOperationsSessionRepositoryTests.java b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcOperationsSessionRepositoryTests.java index 603b4ee7..adf4665c 100644 --- a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcOperationsSessionRepositoryTests.java +++ b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcOperationsSessionRepositoryTests.java @@ -28,6 +28,8 @@ import java.util.function.Supplier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcOperations; @@ -43,24 +45,18 @@ import org.springframework.session.SaveMode; import org.springframework.session.Session; import org.springframework.session.jdbc.JdbcOperationsSessionRepository.JdbcSession; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.TransactionOperations; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.endsWith; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; /** * Tests for {@link JdbcOperationsSessionRepository}. @@ -73,29 +69,39 @@ class JdbcOperationsSessionRepositoryTests { private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; - private JdbcOperations jdbcOperations = mock(JdbcOperations.class); - - private PlatformTransactionManager transactionManager = mock(PlatformTransactionManager.class); + @Mock + private JdbcOperations jdbcOperations; private JdbcOperationsSessionRepository repository; @BeforeEach void setUp() { - this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations, this.transactionManager); + MockitoAnnotations.initMocks(this); + this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations, + TransactionOperations.withoutTransaction()); } @Test void constructorNullJdbcOperations() { assertThatIllegalArgumentException() - .isThrownBy(() -> new JdbcOperationsSessionRepository(null, this.transactionManager)) - .withMessage("JdbcOperations must not be null"); + .isThrownBy(() -> new JdbcOperationsSessionRepository(null, TransactionOperations.withoutTransaction())) + .withMessage("jdbcOperations must not be null"); } @Test - void constructorNullTransactionManager() { + void constructorNullTransactionOperations() { assertThatIllegalArgumentException() - .isThrownBy(() -> new JdbcOperationsSessionRepository(this.jdbcOperations, null)) - .withMessage("TransactionManager must not be null"); + .isThrownBy( + () -> new JdbcOperationsSessionRepository(this.jdbcOperations, (TransactionOperations) null)) + .withMessage("transactionOperations must not be null"); + } + + @Test + @SuppressWarnings("deprecation") + void constructorNullTransactionManager() { + assertThatIllegalArgumentException().isThrownBy( + () -> new JdbcOperationsSessionRepository(this.jdbcOperations, (PlatformTransactionManager) null)) + .withMessage("transactionManager must not be null"); } @Test @@ -248,7 +254,7 @@ class JdbcOperationsSessionRepositoryTests { assertThat(session.isNew()).isTrue(); assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval()); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -260,7 +266,7 @@ class JdbcOperationsSessionRepositoryTests { assertThat(session.isNew()).isTrue(); assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(interval)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -268,7 +274,6 @@ class JdbcOperationsSessionRepositoryTests { this.repository.setFlushMode(FlushMode.IMMEDIATE); JdbcSession session = this.repository.createSession(); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations).update(startsWith("INSERT"), isA(PreparedStatementSetter.class)); verifyNoMoreInteractions(this.jdbcOperations); } @@ -280,9 +285,8 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("INSERT"), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -293,12 +297,11 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION("), isA(PreparedStatementSetter.class)); verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -310,12 +313,11 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION("), isA(PreparedStatementSetter.class)); verify(this.jdbcOperations, times(1)).batchUpdate(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("), isA(BatchPreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -327,10 +329,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -343,10 +344,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).batchUpdate(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("), isA(BatchPreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -360,10 +360,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -379,10 +378,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).batchUpdate(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"), isA(BatchPreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -396,10 +394,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("DELETE FROM SPRING_SESSION_ATTRIBUTES WHERE"), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -411,8 +408,7 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -428,10 +424,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).batchUpdate(startsWith("DELETE FROM SPRING_SESSION_ATTRIBUTES WHERE"), isA(BatchPreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test // gh-1070 @@ -444,10 +439,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test // gh-1070 @@ -460,8 +454,7 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test // gh-1070 @@ -476,10 +469,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations).update(startsWith("DELETE FROM SPRING_SESSION_ATTRIBUTES WHERE"), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test // gh-1070 @@ -494,10 +486,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -509,10 +500,9 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("UPDATE SPRING_SESSION SET"), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -523,7 +513,7 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); assertThat(session.isNew()).isFalse(); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -536,7 +526,6 @@ class JdbcOperationsSessionRepositoryTests { JdbcOperationsSessionRepository.JdbcSession session = this.repository.findById(sessionId); assertThat(session).isNull(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class)); } @@ -552,7 +541,6 @@ class JdbcOperationsSessionRepositoryTests { JdbcOperationsSessionRepository.JdbcSession session = this.repository.findById(expired.getId()); assertThat(session).isNull(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class)); verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), eq(expired.getId())); @@ -571,7 +559,6 @@ class JdbcOperationsSessionRepositoryTests { assertThat(session.getId()).isEqualTo(saved.getId()); assertThat(session.isNew()).isFalse(); assertThat(session.getAttribute("savedName")).isEqualTo("savedValue"); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class)); } @@ -582,7 +569,6 @@ class JdbcOperationsSessionRepositoryTests { this.repository.deleteById(sessionId); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), eq(sessionId)); } @@ -594,7 +580,7 @@ class JdbcOperationsSessionRepositoryTests { .findByIndexNameAndIndexValue("testIndexName", indexValue); assertThat(sessions).isEmpty(); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -608,7 +594,6 @@ class JdbcOperationsSessionRepositoryTests { .findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal); assertThat(sessions).isEmpty(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class)); } @@ -633,7 +618,6 @@ class JdbcOperationsSessionRepositoryTests { .findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal); assertThat(sessions).hasSize(2); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class)); } @@ -642,7 +626,6 @@ class JdbcOperationsSessionRepositoryTests { void cleanupExpiredSessions() { this.repository.cleanUpExpiredSessions(); - assertPropagationRequiresNew(); verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), anyLong()); } @@ -659,84 +642,6 @@ class JdbcOperationsSessionRepositoryTests { assertThat(session.getAttributeNames()).isEmpty(); } - @Test - void saveNewWithoutTransaction() { - this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations); - JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession(); - - this.repository.save(session); - - verify(this.jdbcOperations, times(1)).update(startsWith("INSERT INTO SPRING_SESSION"), - isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); - verifyZeroInteractions(this.transactionManager); - } - - @Test - void saveUpdatedWithoutTransaction() { - this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations); - JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(new MapSession(), - "primaryKey", false); - session.setLastAccessedTime(Instant.now()); - - this.repository.save(session); - - verify(this.jdbcOperations, times(1)).update(startsWith("UPDATE SPRING_SESSION"), - isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); - verifyZeroInteractions(this.transactionManager); - } - - @Test - @SuppressWarnings("unchecked") - void findByIdWithoutTransaction() { - given(this.jdbcOperations.query(anyString(), any(PreparedStatementSetter.class), any(ResultSetExtractor.class))) - .willReturn(Collections.emptyList()); - this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations); - this.repository.findById("testSessionId"); - - verify(this.jdbcOperations, times(1)).query(endsWith("WHERE S.SESSION_ID = ?"), - isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class)); - verifyZeroInteractions(this.jdbcOperations); - verifyZeroInteractions(this.transactionManager); - } - - @Test - void deleteByIdWithoutTransaction() { - this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations); - this.repository.deleteById("testSessionId"); - - verify(this.jdbcOperations, times(1)).update(eq("DELETE FROM SPRING_SESSION WHERE SESSION_ID = ?"), - anyString()); - verifyZeroInteractions(this.jdbcOperations); - verifyZeroInteractions(this.transactionManager); - } - - @Test - @SuppressWarnings("unchecked") - void findByIndexNameAndIndexValueWithoutTransaction() { - given(this.jdbcOperations.query(anyString(), any(PreparedStatementSetter.class), any(ResultSetExtractor.class))) - .willReturn(Collections.emptyList()); - this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations); - this.repository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, - "testIndexValue"); - - verify(this.jdbcOperations, times(1)).query(endsWith("WHERE S.PRINCIPAL_NAME = ?"), - isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class)); - verifyZeroInteractions(this.jdbcOperations); - verifyZeroInteractions(this.transactionManager); - } - - @Test - void cleanUpExpiredSessionsWithoutTransaction() { - this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations); - this.repository.cleanUpExpiredSessions(); - - verify(this.jdbcOperations, times(1)).update(eq("DELETE FROM SPRING_SESSION WHERE EXPIRY_TIME < ?"), anyLong()); - verifyZeroInteractions(this.jdbcOperations); - verifyZeroInteractions(this.transactionManager); - } - @Test void saveWithSaveModeOnSetAttribute() { this.repository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE); @@ -751,7 +656,7 @@ class JdbcOperationsSessionRepositoryTests { this.repository.save(session); verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"), isA(PreparedStatementSetter.class)); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -770,7 +675,7 @@ class JdbcOperationsSessionRepositoryTests { .forClass(BatchPreparedStatementSetter.class); verify(this.jdbcOperations).batchUpdate(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"), captor.capture()); assertThat(captor.getValue().getBatchSize()).isEqualTo(2); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -789,7 +694,7 @@ class JdbcOperationsSessionRepositoryTests { .forClass(BatchPreparedStatementSetter.class); verify(this.jdbcOperations).batchUpdate(startsWith("UPDATE SPRING_SESSION_ATTRIBUTES SET"), captor.capture()); assertThat(captor.getValue().getBatchSize()).isEqualTo(3); - verifyZeroInteractions(this.jdbcOperations); + verifyNoMoreInteractions(this.jdbcOperations); } @Test @@ -798,7 +703,6 @@ class JdbcOperationsSessionRepositoryTests { JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); String attrName = "someAttribute"; session.setAttribute(attrName, "someValue"); - assertPropagationRequiresNew(); verify(this.jdbcOperations).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("), isA(PreparedStatementSetter.class)); verifyNoMoreInteractions(this.jdbcOperations); @@ -811,7 +715,6 @@ class JdbcOperationsSessionRepositoryTests { cached.setAttribute("attribute1", "value1"); JdbcSession session = this.repository.new JdbcSession(cached, "primaryKey", false); session.removeAttribute("attribute1"); - assertPropagationRequiresNew(); verify(this.jdbcOperations).update(startsWith("DELETE FROM SPRING_SESSION_ATTRIBUTES WHERE"), isA(PreparedStatementSetter.class)); verifyNoMoreInteractions(this.jdbcOperations); @@ -822,7 +725,6 @@ class JdbcOperationsSessionRepositoryTests { this.repository.setFlushMode(FlushMode.IMMEDIATE); JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); session.setMaxInactiveInterval(Duration.ofSeconds(1)); - assertPropagationRequiresNew(); verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION SET"), isA(PreparedStatementSetter.class)); verifyNoMoreInteractions(this.jdbcOperations); } @@ -832,16 +734,8 @@ class JdbcOperationsSessionRepositoryTests { this.repository.setFlushMode(FlushMode.IMMEDIATE); JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); session.setLastAccessedTime(Instant.now()); - assertPropagationRequiresNew(); verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION SET"), isA(PreparedStatementSetter.class)); verifyNoMoreInteractions(this.jdbcOperations); } - private void assertPropagationRequiresNew() { - ArgumentCaptor argument = ArgumentCaptor.forClass(TransactionDefinition.class); - verify(this.transactionManager, atLeastOnce()).getTransaction(argument.capture()); - assertThat(argument.getValue().getPropagationBehavior()) - .isEqualTo(TransactionDefinition.PROPAGATION_REQUIRES_NEW); - } - } diff --git a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java index 14aa72d1..96318d16 100644 --- a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java +++ b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfigurationTests.java @@ -37,6 +37,8 @@ import org.springframework.session.jdbc.JdbcOperationsSessionRepository; import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.TransactionOperations; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -77,7 +79,10 @@ class JdbcHttpSessionConfigurationTests { void defaultConfiguration() { registerAndRefresh(DataSourceConfiguration.class, DefaultConfiguration.class); - assertThat(this.context.getBean(JdbcOperationsSessionRepository.class)).isNotNull(); + JdbcOperationsSessionRepository sessionRepository = this.context.getBean(JdbcOperationsSessionRepository.class); + assertThat(sessionRepository).isNotNull(); + assertThat(sessionRepository).extracting("transactionOperations") + .hasFieldOrPropertyWithValue("propagationBehavior", TransactionDefinition.PROPAGATION_REQUIRES_NEW); } @Test @@ -225,6 +230,17 @@ class JdbcHttpSessionConfigurationTests { .withMessageContaining("expected single matching bean but found 2"); } + @Test + void customTransactionOperationsConfiguration() { + registerAndRefresh(DataSourceConfiguration.class, CustomTransactionOperationsConfiguration.class); + + JdbcOperationsSessionRepository repository = this.context.getBean(JdbcOperationsSessionRepository.class); + TransactionOperations transactionOperations = this.context.getBean(TransactionOperations.class); + assertThat(repository).isNotNull(); + assertThat(transactionOperations).isNotNull(); + assertThat(repository).hasFieldOrPropertyWithValue("transactionOperations", transactionOperations); + } + @Test void customLobHandlerConfiguration() { registerAndRefresh(DataSourceConfiguration.class, CustomLobHandlerConfiguration.class); @@ -417,6 +433,16 @@ class JdbcHttpSessionConfigurationTests { } + @EnableJdbcHttpSession + static class CustomTransactionOperationsConfiguration { + + @Bean + public TransactionOperations springSessionTransactionOperations() { + return TransactionOperations.withoutTransaction(); + } + + } + @EnableJdbcHttpSession static class CustomLobHandlerConfiguration {