From 3a972bef767b1d9286b61d8e4a6b02f869fa00d4 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-1151 --- ...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..a9ec61c9 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-1151 + 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-1151 + 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()); }); } }