From c523fb591dc73c7793d2499334abfeeab61fa37f Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Fri, 20 Jul 2018 12:58:33 -0400 Subject: [PATCH] Deserialize attributes lazily in JdbcOperationsSessionRepository Instead of deserializing all of the session attributes as they are read from the database, deserialize as #getAttribute requests them. See: #1133 --- .../jdbc/JdbcOperationsSessionRepository.java | 70 +++++++++++++++---- 1 file changed, 57 insertions(+), 13 deletions(-) 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 0f575c6a..510c7fe0 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 @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.logging.Log; @@ -530,7 +531,7 @@ public class JdbcOperationsSessionRepository implements public void setValues(PreparedStatement ps, int i) throws SQLException { String attributeName = attributeNames.get(i); ps.setString(1, attributeName); - serialize(ps, 2, session.getAttribute(attributeName)); + bytesToLob(ps, 2, serialize(session.getAttribute(attributeName))); ps.setString(3, session.getId()); } @@ -545,7 +546,7 @@ public class JdbcOperationsSessionRepository implements this.jdbcOperations.update(this.createSessionAttributeQuery, (ps) -> { String attributeName = attributeNames.get(0); ps.setString(1, attributeName); - serialize(ps, 2, session.getAttribute(attributeName)); + bytesToLob(ps, 2, serialize(session.getAttribute(attributeName))); ps.setString(3, session.getId()); }); } @@ -559,7 +560,7 @@ public class JdbcOperationsSessionRepository implements @Override public void setValues(PreparedStatement ps, int i) throws SQLException { String attributeName = attributeNames.get(i); - serialize(ps, 1, session.getAttribute(attributeName)); + bytesToLob(ps, 1, serialize(session.getAttribute(attributeName))); ps.setString(2, session.primaryKey); ps.setString(3, attributeName); } @@ -574,7 +575,7 @@ public class JdbcOperationsSessionRepository implements else { this.jdbcOperations.update(this.updateSessionAttributeQuery, (ps) -> { String attributeName = attributeNames.get(0); - serialize(ps, 1, session.getAttribute(attributeName)); + bytesToLob(ps, 1, serialize(session.getAttribute(attributeName))); ps.setString(2, session.primaryKey); ps.setString(3, attributeName); }); @@ -657,18 +658,26 @@ public class JdbcOperationsSessionRepository implements getQuery(DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY); } - private void serialize(PreparedStatement ps, int paramIndex, Object attributeValue) + private void bytesToLob(PreparedStatement ps, int paramIndex, byte[] bytes) throws SQLException { this.lobHandler.getLobCreator().setBlobAsBytes(ps, paramIndex, - (byte[]) this.conversionService.convert(attributeValue, - TypeDescriptor.valueOf(Object.class), - TypeDescriptor.valueOf(byte[].class))); + bytes); } - private Object deserialize(ResultSet rs, String columnName) + private byte[] serialize(Object attributeValue) { + return (byte[]) this.conversionService.convert(attributeValue, + TypeDescriptor.valueOf(Object.class), + TypeDescriptor.valueOf(byte[].class)); + } + + private byte[] lobToBytes(ResultSet rs, String columnName) throws SQLException { + return this.lobHandler.getBlobAsBytes(rs, columnName); + } + + private Object deserialize(byte[] bytes) { return this.conversionService.convert( - this.lobHandler.getBlobAsBytes(rs, columnName), + bytes, TypeDescriptor.valueOf(byte[].class), TypeDescriptor.valueOf(Object.class)); } @@ -679,6 +688,34 @@ public class JdbcOperationsSessionRepository implements } + static final Supplier constantSupplier(Z value) { + if (value == null) { + return null; + } + else { + return () -> value; + } + } + + static final Supplier lazily(Supplier supplier) { + if (supplier == null) { + return null; + } + else { + return new Supplier() { + private Z value; + + @Override + public Z get() { + if (this.value == null) { + this.value = supplier.get(); + } + return this.value; + } + }; + } + } + /** * The {@link Session} to use for {@link JdbcOperationsSessionRepository}. * @@ -748,7 +785,13 @@ public class JdbcOperationsSessionRepository implements @Override public T getAttribute(String attributeName) { - return this.delegate.getAttribute(attributeName); + Supplier supplier = this.delegate.getAttribute(attributeName); + if (supplier == null) { + return null; + } + else { + return supplier.get(); + } } @Override @@ -783,7 +826,7 @@ public class JdbcOperationsSessionRepository implements ? oldDeltaValue : DeltaValue.UPDATED); } - this.delegate.setAttribute(attributeName, attributeValue); + this.delegate.setAttribute(attributeName, constantSupplier(attributeValue)); if (PRINCIPAL_NAME_INDEX_NAME.equals(attributeName) || SPRING_SECURITY_CONTEXT.equals(attributeName)) { this.changed = true; @@ -875,7 +918,8 @@ public class JdbcOperationsSessionRepository implements } String attributeName = rs.getString("ATTRIBUTE_NAME"); if (attributeName != null) { - session.delegate.setAttribute(attributeName, deserialize(rs, "ATTRIBUTE_BYTES")); + byte[] bytes = lobToBytes(rs, "ATTRIBUTE_BYTES"); + session.delegate.setAttribute(attributeName, lazily(() -> deserialize(bytes))); } sessions.add(session); }