diff --git a/spring-session-docs/modules/ROOT/pages/configuration/jdbc.adoc b/spring-session-docs/modules/ROOT/pages/configuration/jdbc.adoc index 7406949ef..1969fb13c 100644 --- a/spring-session-docs/modules/ROOT/pages/configuration/jdbc.adoc +++ b/spring-session-docs/modules/ROOT/pages/configuration/jdbc.adoc @@ -155,13 +155,13 @@ public class QueryCustomizer private static final String CREATE_SESSION_ATTRIBUTE_QUERY = """ INSERT INTO %TABLE_NAME%_ATTRIBUTES (SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) <1> VALUES (?, ?, ?) - ON CONFLICT (SESSION_PRIMARY_ID, ATTRIBUTE_NAME) - DO NOTHING + ON CONFLICT (SESSION_PRIMARY_ID, ATTRIBUTE_NAME) <2> + DO UPDATE SET ATTRIBUTE_BYTES = EXCLUDED.ATTRIBUTE_BYTES """; private static final String UPDATE_SESSION_ATTRIBUTE_QUERY = """ UPDATE %TABLE_NAME%_ATTRIBUTES - SET ATTRIBUTE_BYTES = convert_from(?, 'UTF8')::jsonb + SET ATTRIBUTE_BYTES = encode(?, 'escape') WHERE SESSION_PRIMARY_ID = ? AND ATTRIBUTE_NAME = ? """; @@ -177,6 +177,7 @@ public class QueryCustomizer ====== <1> The `%TABLE_NAME%` placeholder in the query will be replaced by the configured table name being used by `JdbcIndexedSessionRepository`. +<2> `ON CONFLICT` handles duplicate keys by updating the existing row instead of throwing an error, making the insert operation idempotent. [TIP] ==== @@ -310,6 +311,8 @@ public class SessionConfig { private static final String CREATE_SESSION_ATTRIBUTE_QUERY = """ INSERT INTO %TABLE_NAME%_ATTRIBUTES (SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) VALUES (?, ?, convert_from(?, 'UTF8')::jsonb) <1> + ON CONFLICT (SESSION_PRIMARY_ID, ATTRIBUTE_NAME) + DO UPDATE SET ATTRIBUTE_BYTES = EXCLUDED.ATTRIBUTE_BYTES """; private static final String UPDATE_SESSION_ATTRIBUTE_QUERY = """ @@ -331,7 +334,7 @@ public class SessionConfig { ---- ====== -<1> Uses the https://www.postgresql.org/docs/current/functions-binarystring.html[PostgreSQL encode] function to convert from `bytea` to `text` +<1> Uses the https://www.postgresql.org/docs/current/functions-binarystring.html[PostgreSQL convert_from] function to convert the `bytea` value to `text`, which is then cast to `jsonb`. And that's it, you should now be able to see the session attributes saved as JSON in the database. There is a https://github.com/spring-projects/spring-session/tree/main/spring-session-samples/spring-session-sample-boot-jdbc-json-attribute[sample available] where you can see the whole implementation and run the tests. diff --git a/spring-session-samples/spring-session-sample-boot-jdbc-json-attribute/src/main/java/sample/config/SessionConfig.java b/spring-session-samples/spring-session-sample-boot-jdbc-json-attribute/src/main/java/sample/config/SessionConfig.java index 5c9acc630..0adbe9af2 100644 --- a/spring-session-samples/spring-session-sample-boot-jdbc-json-attribute/src/main/java/sample/config/SessionConfig.java +++ b/spring-session-samples/spring-session-sample-boot-jdbc-json-attribute/src/main/java/sample/config/SessionConfig.java @@ -24,11 +24,13 @@ public class SessionConfig implements BeanClassLoaderAware { private static final String CREATE_SESSION_ATTRIBUTE_QUERY = """ INSERT INTO %TABLE_NAME%_ATTRIBUTES (SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) VALUES (?, ?, convert_from(?, 'UTF8')::jsonb) + ON CONFLICT (SESSION_PRIMARY_ID, ATTRIBUTE_NAME) + DO UPDATE SET ATTRIBUTE_BYTES = EXCLUDED.ATTRIBUTE_BYTES """; private static final String UPDATE_SESSION_ATTRIBUTE_QUERY = """ UPDATE %TABLE_NAME%_ATTRIBUTES - SET ATTRIBUTE_BYTES = encode(?, 'escape')::jsonb + SET ATTRIBUTE_BYTES = convert_from(?, 'UTF8')::jsonb WHERE SESSION_PRIMARY_ID = ? AND ATTRIBUTE_NAME = ? """;