From 46bac131d0f301a760cfa03d5a5a735e4a21e4ff Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Tue, 25 Sep 2018 18:45:02 +0200 Subject: [PATCH] Configure default LobHandler to use temporary LOBs on Oracle JdbcOperationsSessionRepository recently introduced validation when inserting new session attributes in order to prevent data integrity violations in highly concurrent environments. This is done by using INSERT INTO ... SELECT statement to verify existence of session record in parent table. Such arrangement causes problems with Oracle if inserted attribute is of size 4 kb or more. This commit enhances JdbcHttpSessionConfiguration to detect Oracle database is used, and set createTemporaryLob option on default LobHandler to true. Resolves: #1212 --- ...JdbcOperationsSessionRepositoryITests.java | 15 +++++++++++ .../http/JdbcHttpSessionConfiguration.java | 26 ++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/spring-session/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcOperationsSessionRepositoryITests.java b/spring-session/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcOperationsSessionRepositoryITests.java index b9522fd1..9604e00e 100644 --- a/spring-session/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcOperationsSessionRepositoryITests.java +++ b/spring-session/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcOperationsSessionRepositoryITests.java @@ -633,6 +633,21 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests { assertThat(this.repository.getSession(session.getId())).isNull(); } + @Test // gh-1203 + public void saveWithLargeAttribute() { + String attributeName = "largeAttribute"; + int arraySize = 4000; + + JdbcOperationsSessionRepository.JdbcSession session = this.repository + .createSession(); + session.setAttribute(attributeName, new byte[arraySize]); + this.repository.save(session); + session = this.repository.getSession(session.getId()); + + assertThat(session).isNotNull(); + assertThat((byte[]) session.getAttribute(attributeName)).hasSize(arraySize); + } + private String getSecurityName() { return this.context.getAuthentication().getName(); } diff --git a/spring-session/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java b/spring-session/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java index 759df031..57744ffc 100644 --- a/spring-session/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java +++ b/spring-session/src/main/java/org/springframework/session/jdbc/config/annotation/web/http/JdbcHttpSessionConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2018 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. @@ -33,8 +33,10 @@ import org.springframework.core.convert.support.GenericConversionService; import org.springframework.core.serializer.support.DeserializingConverter; import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.JdbcUtils; +import org.springframework.jdbc.support.MetaDataAccessException; +import org.springframework.jdbc.support.lob.DefaultLobHandler; import org.springframework.jdbc.support.lob.LobHandler; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; @@ -85,7 +87,7 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration @Bean public JdbcOperationsSessionRepository sessionRepository( - @Qualifier("springSessionJdbcOperations") JdbcOperations jdbcOperations, + @Qualifier("springSessionJdbcOperations") JdbcTemplate jdbcOperations, PlatformTransactionManager transactionManager) { JdbcOperationsSessionRepository sessionRepository = new JdbcOperationsSessionRepository(jdbcOperations, transactionManager); @@ -98,6 +100,11 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration if (this.lobHandler != null) { sessionRepository.setLobHandler(this.lobHandler); } + else if (requiresTemporaryLob(jdbcOperations.getDataSource())) { + DefaultLobHandler lobHandler = new DefaultLobHandler(); + lobHandler.setCreateTemporaryLob(true); + sessionRepository.setLobHandler(lobHandler); + } if (this.springSessionConversionService != null) { sessionRepository.setConversionService(this.springSessionConversionService); } @@ -111,11 +118,22 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration return sessionRepository; } + private static boolean requiresTemporaryLob(DataSource dataSource) { + try { + String productName = (String) JdbcUtils.extractDatabaseMetaData(dataSource, + "getDatabaseProductName"); + return "Oracle".equalsIgnoreCase(JdbcUtils.commonDatabaseName(productName)); + } + catch (MetaDataAccessException ex) { + return false; + } + } + /** * This must be a separate method because some ClassLoaders load the entire method * definition even if an if statement guards against it loading. This means that older * versions of Spring would cause a NoSuchMethodError if this were defined in - * {@link #sessionRepository(JdbcOperations, PlatformTransactionManager)}. + * {@link #sessionRepository(JdbcTemplate, PlatformTransactionManager)}. * * @return the default {@link ConversionService} */