diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java index 8b4b0df5cd..d1dcb94795 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,23 +43,32 @@ import org.springframework.util.Assert; public abstract class NamedParameterUtils { /** - * Set of characters that qualify as parameter separators, - * indicating that a parameter name in a SQL String has ended. + * Set of characters that qualify as comment or quotes starting characters. */ - private static final char[] PARAMETER_SEPARATORS = - new char[] {'"', '\'', ':', '&', ',', ';', '(', ')', '|', '=', '+', '-', '*', '%', '/', '\\', '<', '>', '^'}; + private static final String[] START_SKIP = new String[] {"'", "\"", "--", "/*"}; /** - * Set of characters that qualify as comment or quotes starting characters. + * Set of characters that at are the corresponding comment or quotes ending characters. */ - private static final String[] START_SKIP = - new String[] {"'", "\"", "--", "/*"}; + private static final String[] STOP_SKIP = new String[] {"'", "\"", "\n", "*/"}; /** - * Set of characters that at are the corresponding comment or quotes ending characters. + * Set of characters that qualify as parameter separators, + * indicating that a parameter name in a SQL String has ended. + */ + private static final String PARAMETER_SEPARATORS = "\"':&,;()[]|=+-*%/\\<>^"; + + /** + * An index with separator flags per character code. + * Technically only needed between 34 and 124 at this point. */ - private static final String[] STOP_SKIP = - new String[] {"'", "\"", "\n", "*/"}; + private static final boolean[] separatorIndex = new boolean[128]; + + static { + for (char c : PARAMETER_SEPARATORS.toCharArray()) { + separatorIndex[c] = true; + } + } //------------------------------------------------------------------------- @@ -233,7 +242,6 @@ public abstract class NamedParameterUtils { // character sequence ending comment or quote not found return statement.length; } - } } return position; @@ -384,15 +392,7 @@ public abstract class NamedParameterUtils { * that is, whether the given character qualifies as a separator. */ private static boolean isParameterSeparator(char c) { - if (Character.isWhitespace(c)) { - return true; - } - for (char separator : PARAMETER_SEPARATORS) { - if (c == separator) { - return true; - } - } - return false; + return (separatorIndex[c] || Character.isWhitespace(c)); } /** @@ -425,8 +425,8 @@ public abstract class NamedParameterUtils { List paramNames = parsedSql.getParameterNames(); List params = new LinkedList<>(); for (String paramName : paramNames) { - params.add( - new SqlParameter(paramName, paramSource.getSqlType(paramName), paramSource.getTypeName(paramName))); + params.add(new SqlParameter( + paramName, paramSource.getSqlType(paramName), paramSource.getTypeName(paramName))); } return params; } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java index 33af4c29f8..e14f4b5a06 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -47,32 +48,45 @@ import static org.mockito.BDDMockito.*; * @author Rick Evans * @author Juergen Hoeller * @author Chris Beams + * @author Nikita Khateev */ public class NamedParameterJdbcTemplateTests { private static final String SELECT_NAMED_PARAMETERS = - "select id, forename from custmr where id = :id and country = :country"; + "select id, forename from custmr where id = :id and country = :country"; private static final String SELECT_NAMED_PARAMETERS_PARSED = - "select id, forename from custmr where id = ? and country = ?"; + "select id, forename from custmr where id = ? and country = ?"; private static final String SELECT_NO_PARAMETERS = "select id, forename from custmr"; private static final String UPDATE_NAMED_PARAMETERS = - "update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId"; + "update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId"; private static final String UPDATE_NAMED_PARAMETERS_PARSED = - "update seat_status set booking_id = null where performance_id = ? and price_band_id = ?"; + "update seat_status set booking_id = null where performance_id = ? and price_band_id = ?"; + + private static final String UPDATE_ARRAY_PARAMETERS = + "update customer set type = array[:typeIds] where id = :id"; + private static final String UPDATE_ARRAY_PARAMETERS_PARSED = + "update customer set type = array[?, ?, ?] where id = ?"; private static final String[] COLUMN_NAMES = new String[] {"id", "forename"}; + @Rule public ExpectedException thrown = ExpectedException.none(); private Connection connection; + private DataSource dataSource; + private PreparedStatement preparedStatement; + private ResultSet resultSet; + private DatabaseMetaData databaseMetaData; + private Map params = new HashMap<>(); + private NamedParameterJdbcTemplate namedParameterTemplate; @@ -131,6 +145,31 @@ public class NamedParameterJdbcTemplateTests { verify(connection).close(); } + @Test + public void testExecuteArray() throws SQLException { + given(preparedStatement.executeUpdate()).willReturn(1); + + List typeIds = Arrays.asList(1, 2, 3); + + params.put("typeIds", typeIds); + params.put("id", 1); + Object result = namedParameterTemplate.execute(UPDATE_ARRAY_PARAMETERS, params, + (PreparedStatementCallback) ps -> { + assertEquals(preparedStatement, ps); + ps.executeUpdate(); + return "result"; + }); + + assertEquals("result", result); + verify(connection).prepareStatement(UPDATE_ARRAY_PARAMETERS_PARSED); + verify(preparedStatement).setObject(1, 1); + verify(preparedStatement).setObject(2, 2); + verify(preparedStatement).setObject(3, 3); + verify(preparedStatement).setObject(4, 1); + verify(preparedStatement).close(); + verify(connection).close(); + } + @Test public void testExecuteWithTypedParameters() throws SQLException { given(preparedStatement.executeUpdate()).willReturn(1);