From cb6f7fdfa6b7ebf29b67b7640aa9493254127d70 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-1153 --- ...JdbcOperationsSessionRepositoryITests.java | 25 +++++++++++++++++++ .../jdbc/JdbcOperationsSessionRepository.java | 16 ++++++------ 2 files changed, 34 insertions(+), 7 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 d2023784..b9522fd1 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 @@ -608,6 +608,31 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests { assertThat(this.repository.getSession(session.getId())).isNull(); } + @Test // gh-1153 + public void saveDeleted() { + JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession(); + this.repository.save(session); + session = this.repository.getSession(session.getId()); + this.repository.delete(session.getId()); + session.setLastAccessedTime(System.currentTimeMillis()); + this.repository.save(session); + + assertThat(this.repository.getSession(session.getId())).isNull(); + } + + @Test // gh-1153 + public void saveDeletedAddAttribute() { + JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession(); + this.repository.save(session); + session = this.repository.getSession(session.getId()); + this.repository.delete(session.getId()); + session.setLastAccessedTime(System.currentTimeMillis()); + session.setAttribute("testName", "testValue1"); + this.repository.save(session); + + assertThat(this.repository.getSession(session.getId())).isNull(); + } + private String getSecurityName() { return this.context.getAuthentication().getName(); } diff --git a/spring-session/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java b/spring-session/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java index 4ef1f6c1..cb102b87 100644 --- a/spring-session/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java +++ b/spring-session/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java @@ -143,7 +143,9 @@ public class JdbcOperationsSessionRepository implements private static final String CREATE_SESSION_ATTRIBUTE_QUERY = "INSERT INTO %TABLE_NAME%_ATTRIBUTES(SESSION_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) " + - "VALUES (?, ?, ?)"; + "SELECT SESSION_ID, ?, ? " + + "FROM %TABLE_NAME% " + + "WHERE SESSION_ID = ?"; private static final String GET_SESSION_QUERY = "SELECT S.SESSION_ID, S.CREATION_TIME, S.LAST_ACCESS_TIME, S.MAX_INACTIVE_INTERVAL, SA.ATTRIBUTE_NAME, SA.ATTRIBUTE_BYTES " + @@ -398,9 +400,9 @@ public class JdbcOperationsSessionRepository implements public void setValues(PreparedStatement ps, int i) throws SQLException { String attributeName = attributeNames.get(i); - ps.setString(1, session.getId()); - 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()); } public int getBatchSize() { @@ -465,9 +467,9 @@ public class JdbcOperationsSessionRepository implements new PreparedStatementSetter() { public void setValues(PreparedStatement ps) throws SQLException { - ps.setString(1, session.getId()); - ps.setString(2, entry.getKey()); - serialize(ps, 3, entry.getValue()); + ps.setString(1, entry.getKey()); + serialize(ps, 2, entry.getValue()); + ps.setString(3, session.getId()); } });