From 87c2e53b5a31d08b9010b61479fdfb1f8b7c3774 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Mon, 13 Aug 2018 06:28:42 +0200 Subject: [PATCH] Insert new attributes conditionally in JDBC repo At present, the insert of new attributes in JdbcOperationsSessionRepository is done unconditionally. This can cause data integrity violation errors with concurrent requests, where one request attempts to add new session attribute while the other, concurrent request, deletes the session. This commit addresses the described scenario by executing insert of new attributes conditionally on presence of parent record. Closes gh-1031 --- ...JdbcOperationsSessionRepositoryITests.java | 25 +++++++++++++++++++ .../jdbc/JdbcOperationsSessionRepository.java | 16 ++++++------ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/spring-session-jdbc/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcOperationsSessionRepositoryITests.java b/spring-session-jdbc/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcOperationsSessionRepositoryITests.java index 1a9ea141..cf79c1f2 100644 --- a/spring-session-jdbc/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcOperationsSessionRepositoryITests.java +++ b/spring-session-jdbc/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcOperationsSessionRepositoryITests.java @@ -743,6 +743,31 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests { assertThat(session.getAttribute("testName")).isEqualTo("testValue2"); } + @Test // gh-1031 + public void saveDeleted() { + JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession(); + this.repository.save(session); + session = this.repository.findById(session.getId()); + this.repository.deleteById(session.getId()); + session.setLastAccessedTime(Instant.now()); + this.repository.save(session); + + assertThat(this.repository.findById(session.getId())).isNull(); + } + + @Test // gh-1031 + public void saveDeletedAddAttribute() { + JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession(); + this.repository.save(session); + session = this.repository.findById(session.getId()); + this.repository.deleteById(session.getId()); + session.setLastAccessedTime(Instant.now()); + session.setAttribute("testName", "testValue1"); + this.repository.save(session); + + assertThat(this.repository.findById(session.getId())).isNull(); + } + private String getSecurityName() { return this.context.getAuthentication().getName(); } 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 f34fab84..41325281 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 @@ -144,7 +144,9 @@ public class JdbcOperationsSessionRepository implements private static final String CREATE_SESSION_ATTRIBUTE_QUERY = "INSERT INTO %TABLE_NAME%_ATTRIBUTES(SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) " + - "VALUES (?, ?, ?)"; + "SELECT PRIMARY_ID, ?, ? " + + "FROM %TABLE_NAME% " + + "WHERE SESSION_ID = ?"; private static final String GET_SESSION_QUERY = "SELECT S.PRIMARY_ID, S.SESSION_ID, S.CREATION_TIME, S.LAST_ACCESS_TIME, S.MAX_INACTIVE_INTERVAL, SA.ATTRIBUTE_NAME, SA.ATTRIBUTE_BYTES " + @@ -499,9 +501,9 @@ public class JdbcOperationsSessionRepository implements @Override public void setValues(PreparedStatement ps, int i) throws SQLException { String attributeName = attributeNames.get(i); - ps.setString(1, session.primaryKey); - ps.setString(2, attributeName); - serialize(ps, 3, session.getAttribute(attributeName)); + ps.setString(1, attributeName); + serialize(ps, 2, session.getAttribute(attributeName)); + ps.setString(3, session.getId()); } @Override @@ -514,9 +516,9 @@ public class JdbcOperationsSessionRepository implements else { this.jdbcOperations.update(this.createSessionAttributeQuery, (ps) -> { String attributeName = attributeNames.get(0); - ps.setString(1, session.primaryKey); - ps.setString(2, attributeName); - serialize(ps, 3, session.getAttribute(attributeName)); + ps.setString(1, attributeName); + serialize(ps, 2, session.getAttribute(attributeName)); + ps.setString(3, session.getId()); }); } }