Skip to content

Commit

Permalink
Add custom SQL JdbcOperationsSessionRepository
Browse files Browse the repository at this point in the history
This commit allows custom SQL queries for
JdbcOperationsSessionRepository.

Fixes spring-projectsgh-609
  • Loading branch information
vpavic authored and Rob Winch committed Sep 12, 2016
1 parent 6335894 commit 26eca5b
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 11 deletions.
1 change: 1 addition & 0 deletions docs/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,7 @@ However, you can override the default `ConversionService` by providing a Bean na

By default, this implementation uses `SPRING_SESSION` and `SPRING_SESSION_ATTRIBUTES` tables to store sessions.
Note that the table name can be easily customized as already described. In that case the table used to store attributes will be named using the provided table name, suffixed with `_ATTRIBUTES`.
If further customizations are needed, SQL queries used by the repository can be customized using `set*Query` setter methods. In this case you need to manually configure the `sessionRepository` bean.

Due to the differences between the various database vendors, especially when it comes to storing binary data, make sure to use SQL script specific to your database.
Scripts for most major database vendors are packaged as `org/springframework/session/jdbc/schema-\*.sql`, where `*` is the target database type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,24 @@ public class JdbcOperationsSessionRepository implements
*/
private String tableName = DEFAULT_TABLE_NAME;

private String createSessionQuery;

private String createSessionAttributeQuery;

private String getSessionQuery;

private String updateSessionQuery;

private String updateSessionAttributeQuery;

private String deleteSessionAttributeQuery;

private String deleteSessionQuery;

private String listSessionsByPrincipalNameQuery;

private String deleteSessionsByLastAccessTimeQuery;

/**
* If non-null, this value is used to override the default value for
* {@link JdbcSession#setMaxInactiveIntervalInSeconds(int)}.
Expand Down Expand Up @@ -229,6 +247,7 @@ public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations,
this.jdbcOperations = jdbcOperations;
this.transactionOperations = createTransactionTemplate(transactionManager);
this.conversionService = createDefaultConversionService();
prepareQueries();
}

/**
Expand All @@ -238,6 +257,88 @@ public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations,
public void setTableName(String tableName) {
Assert.hasText(tableName, "Table name must not be empty");
this.tableName = tableName.trim();
prepareQueries();
}

/**
* Set the custom SQL query used to create the session.
* @param createSessionQuery the SQL query string
*/
public void setCreateSessionQuery(String createSessionQuery) {
Assert.hasText(createSessionQuery, "Query must not be empty");
this.createSessionQuery = createSessionQuery;
}

/**
* Set the custom SQL query used to create the session attribute.
* @param createSessionAttributeQuery the SQL query string
*/
public void setCreateSessionAttributeQuery(String createSessionAttributeQuery) {
Assert.hasText(createSessionAttributeQuery, "Query must not be empty");
this.createSessionAttributeQuery = createSessionAttributeQuery;
}

/**
* Set the custom SQL query used to retrieve the session.
* @param getSessionQuery the SQL query string
*/
public void setGetSessionQuery(String getSessionQuery) {
Assert.hasText(getSessionQuery, "Query must not be empty");
this.getSessionQuery = getSessionQuery;
}

/**
* Set the custom SQL query used to update the session.
* @param updateSessionQuery the SQL query string
*/
public void setUpdateSessionQuery(String updateSessionQuery) {
Assert.hasText(updateSessionQuery, "Query must not be empty");
this.updateSessionQuery = updateSessionQuery;
}

/**
* Set the custom SQL query used to update the session attribute.
* @param updateSessionAttributeQuery the SQL query string
*/
public void setUpdateSessionAttributeQuery(String updateSessionAttributeQuery) {
Assert.hasText(updateSessionAttributeQuery, "Query must not be empty");
this.updateSessionAttributeQuery = updateSessionAttributeQuery;
}

/**
* Set the custom SQL query used to delete the session attribute.
* @param deleteSessionAttributeQuery the SQL query string
*/
public void setDeleteSessionAttributeQuery(String deleteSessionAttributeQuery) {
Assert.hasText(deleteSessionAttributeQuery, "Query must not be empty");
this.deleteSessionAttributeQuery = deleteSessionAttributeQuery;
}

/**
* Set the custom SQL query used to delete the session.
* @param deleteSessionQuery the SQL query string
*/
public void setDeleteSessionQuery(String deleteSessionQuery) {
Assert.hasText(deleteSessionQuery, "Query must not be empty");
this.deleteSessionQuery = deleteSessionQuery;
}

/**
* Set the custom SQL query used to retrieve the sessions by principal name.
* @param listSessionsByPrincipalNameQuery the SQL query string
*/
public void setListSessionsByPrincipalNameQuery(String listSessionsByPrincipalNameQuery) {
Assert.hasText(listSessionsByPrincipalNameQuery, "Query must not be empty");
this.listSessionsByPrincipalNameQuery = listSessionsByPrincipalNameQuery;
}

/**
* Set the custom SQL query used to delete the sessions by last access time.
* @param deleteSessionsByLastAccessTimeQuery the SQL query string
*/
public void setDeleteSessionsByLastAccessTimeQuery(String deleteSessionsByLastAccessTimeQuery) {
Assert.hasText(deleteSessionsByLastAccessTimeQuery, "Query must not be empty");
this.deleteSessionsByLastAccessTimeQuery = deleteSessionsByLastAccessTimeQuery;
}

/**
Expand Down Expand Up @@ -278,7 +379,7 @@ public void save(final JdbcSession session) {

protected void doInTransactionWithoutResult(TransactionStatus status) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(CREATE_SESSION_QUERY),
JdbcOperationsSessionRepository.this.createSessionQuery,
new PreparedStatementSetter() {

public void setValues(PreparedStatement ps) throws SQLException {
Expand All @@ -293,7 +394,7 @@ public void setValues(PreparedStatement ps) throws SQLException {
if (!session.getAttributeNames().isEmpty()) {
final List<String> attributeNames = new ArrayList<String>(session.getAttributeNames());
JdbcOperationsSessionRepository.this.jdbcOperations.batchUpdate(
getQuery(CREATE_SESSION_ATTRIBUTE_QUERY),
JdbcOperationsSessionRepository.this.createSessionAttributeQuery,
new BatchPreparedStatementSetter() {

public void setValues(PreparedStatement ps, int i) throws SQLException {
Expand All @@ -319,7 +420,7 @@ public int getBatchSize() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
if (session.isChanged()) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(UPDATE_SESSION_QUERY),
JdbcOperationsSessionRepository.this.updateSessionQuery,
new PreparedStatementSetter() {

public void setValues(PreparedStatement ps)
Expand All @@ -337,7 +438,7 @@ public void setValues(PreparedStatement ps)
for (final Map.Entry<String, Object> entry : delta.entrySet()) {
if (entry.getValue() == null) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(DELETE_SESSION_ATTRIBUTE_QUERY),
JdbcOperationsSessionRepository.this.deleteSessionAttributeQuery,
new PreparedStatementSetter() {

public void setValues(PreparedStatement ps) throws SQLException {
Expand All @@ -349,7 +450,7 @@ public void setValues(PreparedStatement ps) throws SQLException {
}
else {
int updatedCount = JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(UPDATE_SESSION_ATTRIBUTE_QUERY),
JdbcOperationsSessionRepository.this.updateSessionAttributeQuery,
new PreparedStatementSetter() {

public void setValues(PreparedStatement ps) throws SQLException {
Expand All @@ -361,7 +462,7 @@ public void setValues(PreparedStatement ps) throws SQLException {
});
if (updatedCount == 0) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(CREATE_SESSION_ATTRIBUTE_QUERY),
JdbcOperationsSessionRepository.this.createSessionAttributeQuery,
new PreparedStatementSetter() {

public void setValues(PreparedStatement ps) throws SQLException {
Expand All @@ -387,7 +488,7 @@ public JdbcSession getSession(final String id) {

public ExpiringSession doInTransaction(TransactionStatus status) {
List<ExpiringSession> sessions = JdbcOperationsSessionRepository.this.jdbcOperations.query(
getQuery(GET_SESSION_QUERY),
JdbcOperationsSessionRepository.this.getSessionQuery,
new PreparedStatementSetter() {

public void setValues(PreparedStatement ps) throws SQLException {
Expand Down Expand Up @@ -421,7 +522,7 @@ public void delete(final String id) {

protected void doInTransactionWithoutResult(TransactionStatus status) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(DELETE_SESSION_QUERY), id);
JdbcOperationsSessionRepository.this.deleteSessionQuery, id);
}

});
Expand All @@ -437,7 +538,7 @@ public Map<String, JdbcSession> findByIndexNameAndIndexValue(String indexName,

public List<ExpiringSession> doInTransaction(TransactionStatus status) {
return JdbcOperationsSessionRepository.this.jdbcOperations.query(
getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY),
JdbcOperationsSessionRepository.this.listSessionsByPrincipalNameQuery,
new PreparedStatementSetter() {

public void setValues(PreparedStatement ps) throws SQLException {
Expand Down Expand Up @@ -479,7 +580,7 @@ public void cleanUpExpiredSessions() {

public Integer doInTransaction(TransactionStatus transactionStatus) {
return JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY),
JdbcOperationsSessionRepository.this.deleteSessionsByLastAccessTimeQuery,
sessionsValidFromTime);
}

Expand Down Expand Up @@ -515,10 +616,24 @@ private static GenericConversionService createDefaultConversionService() {
return converter;
}

protected String getQuery(String base) {
private String getQuery(String base) {
return StringUtils.replace(base, "%TABLE_NAME%", this.tableName);
}

private void prepareQueries() {
this.createSessionQuery = getQuery(CREATE_SESSION_QUERY);
this.createSessionAttributeQuery = getQuery(CREATE_SESSION_ATTRIBUTE_QUERY);
this.getSessionQuery = getQuery(GET_SESSION_QUERY);
this.updateSessionQuery = getQuery(UPDATE_SESSION_QUERY);
this.updateSessionAttributeQuery = getQuery(UPDATE_SESSION_ATTRIBUTE_QUERY);
this.deleteSessionAttributeQuery = getQuery(DELETE_SESSION_ATTRIBUTE_QUERY);
this.deleteSessionQuery = getQuery(DELETE_SESSION_QUERY);
this.listSessionsByPrincipalNameQuery =
getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY);
this.deleteSessionsByLastAccessTimeQuery =
getQuery(DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY);
}

private void serialize(PreparedStatement ps, int paramIndex, Object attributeValue)
throws SQLException {
this.lobHandler.getLobCreator().setBlobAsBytes(ps, paramIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,150 @@ public void setTableNameEmpty() {
this.repository.setTableName(" ");
}

@Test
public void setCreateSessionQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setCreateSessionQuery(null);
}

@Test
public void setCreateSessionQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setCreateSessionQuery(" ");
}

@Test
public void setCreateSessionAttributeQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setCreateSessionAttributeQuery(null);
}

@Test
public void setCreateSessionAttributeQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setCreateSessionAttributeQuery(" ");
}

@Test
public void setGetSessionQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setGetSessionQuery(null);
}

@Test
public void setGetSessionQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setGetSessionQuery(" ");
}

@Test
public void setUpdateSessionQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setUpdateSessionQuery(null);
}

@Test
public void setUpdateSessionQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setUpdateSessionQuery(" ");
}

@Test
public void setUpdateSessionAttributeQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setUpdateSessionAttributeQuery(null);
}

@Test
public void setUpdateSessionAttributeQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setUpdateSessionAttributeQuery(" ");
}

@Test
public void setDeleteSessionAttributeQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setDeleteSessionAttributeQuery(null);
}

@Test
public void setDeleteSessionAttributeQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setDeleteSessionAttributeQuery(" ");
}

@Test
public void setDeleteSessionQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setDeleteSessionQuery(null);
}

@Test
public void setDeleteSessionQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setDeleteSessionQuery(" ");
}

@Test
public void setListSessionsByPrincipalNameQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setListSessionsByPrincipalNameQuery(null);
}

@Test
public void setListSessionsByPrincipalNameQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setListSessionsByPrincipalNameQuery(" ");
}

@Test
public void setDeleteSessionsByLastAccessTimeQueryNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setDeleteSessionsByLastAccessTimeQuery(null);
}

@Test
public void setDeleteSessionsByLastAccessTimeQueryEmpty() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Query must not be empty");

this.repository.setDeleteSessionsByLastAccessTimeQuery(" ");
}

@Test
public void setLobHandlerNull() {
this.thrown.expect(IllegalArgumentException.class);
Expand Down

0 comments on commit 26eca5b

Please sign in to comment.