001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.jdbc.core.namedparam;
018
019import java.util.Collections;
020import java.util.LinkedHashMap;
021import java.util.Map;
022
023import org.springframework.jdbc.core.SqlParameterValue;
024import org.springframework.lang.NonNull;
025import org.springframework.lang.Nullable;
026import org.springframework.util.Assert;
027import org.springframework.util.StringUtils;
028
029/**
030 * {@link SqlParameterSource} implementation that holds a given Map of parameters.
031 *
032 * <p>This class is intended for passing in a simple Map of parameter values
033 * to the methods of the {@link NamedParameterJdbcTemplate} class.
034 *
035 * <p>The {@code addValue} methods on this class will make adding several values
036 * easier. The methods return a reference to the {@link MapSqlParameterSource}
037 * itself, so you can chain several method calls together within a single statement.
038 *
039 * @author Thomas Risberg
040 * @author Juergen Hoeller
041 * @since 2.0
042 * @see #addValue(String, Object)
043 * @see #addValue(String, Object, int)
044 * @see #registerSqlType
045 * @see NamedParameterJdbcTemplate
046 */
047public class MapSqlParameterSource extends AbstractSqlParameterSource {
048
049        private final Map<String, Object> values = new LinkedHashMap<>();
050
051
052        /**
053         * Create an empty MapSqlParameterSource,
054         * with values to be added via {@code addValue}.
055         * @see #addValue(String, Object)
056         */
057        public MapSqlParameterSource() {
058        }
059
060        /**
061         * Create a new MapSqlParameterSource, with one value
062         * comprised of the supplied arguments.
063         * @param paramName the name of the parameter
064         * @param value the value of the parameter
065         * @see #addValue(String, Object)
066         */
067        public MapSqlParameterSource(String paramName, @Nullable Object value) {
068                addValue(paramName, value);
069        }
070
071        /**
072         * Create a new MapSqlParameterSource based on a Map.
073         * @param values a Map holding existing parameter values (can be {@code null})
074         */
075        public MapSqlParameterSource(@Nullable Map<String, ?> values) {
076                addValues(values);
077        }
078
079
080        /**
081         * Add a parameter to this parameter source.
082         * @param paramName the name of the parameter
083         * @param value the value of the parameter
084         * @return a reference to this parameter source,
085         * so it's possible to chain several calls together
086         */
087        public MapSqlParameterSource addValue(String paramName, @Nullable Object value) {
088                Assert.notNull(paramName, "Parameter name must not be null");
089                this.values.put(paramName, value);
090                if (value instanceof SqlParameterValue) {
091                        registerSqlType(paramName, ((SqlParameterValue) value).getSqlType());
092                }
093                return this;
094        }
095
096        /**
097         * Add a parameter to this parameter source.
098         * @param paramName the name of the parameter
099         * @param value the value of the parameter
100         * @param sqlType the SQL type of the parameter
101         * @return a reference to this parameter source,
102         * so it's possible to chain several calls together
103         */
104        public MapSqlParameterSource addValue(String paramName, @Nullable Object value, int sqlType) {
105                Assert.notNull(paramName, "Parameter name must not be null");
106                this.values.put(paramName, value);
107                registerSqlType(paramName, sqlType);
108                return this;
109        }
110
111        /**
112         * Add a parameter to this parameter source.
113         * @param paramName the name of the parameter
114         * @param value the value of the parameter
115         * @param sqlType the SQL type of the parameter
116         * @param typeName the type name of the parameter
117         * @return a reference to this parameter source,
118         * so it's possible to chain several calls together
119         */
120        public MapSqlParameterSource addValue(String paramName, @Nullable Object value, int sqlType, String typeName) {
121                Assert.notNull(paramName, "Parameter name must not be null");
122                this.values.put(paramName, value);
123                registerSqlType(paramName, sqlType);
124                registerTypeName(paramName, typeName);
125                return this;
126        }
127
128        /**
129         * Add a Map of parameters to this parameter source.
130         * @param values a Map holding existing parameter values (can be {@code null})
131         * @return a reference to this parameter source,
132         * so it's possible to chain several calls together
133         */
134        public MapSqlParameterSource addValues(@Nullable Map<String, ?> values) {
135                if (values != null) {
136                        values.forEach((key, value) -> {
137                                this.values.put(key, value);
138                                if (value instanceof SqlParameterValue) {
139                                        registerSqlType(key, ((SqlParameterValue) value).getSqlType());
140                                }
141                        });
142                }
143                return this;
144        }
145
146        /**
147         * Expose the current parameter values as read-only Map.
148         */
149        public Map<String, Object> getValues() {
150                return Collections.unmodifiableMap(this.values);
151        }
152
153
154        @Override
155        public boolean hasValue(String paramName) {
156                return this.values.containsKey(paramName);
157        }
158
159        @Override
160        @Nullable
161        public Object getValue(String paramName) {
162                if (!hasValue(paramName)) {
163                        throw new IllegalArgumentException("No value registered for key '" + paramName + "'");
164                }
165                return this.values.get(paramName);
166        }
167
168        @Override
169        @NonNull
170        public String[] getParameterNames() {
171                return StringUtils.toStringArray(this.values.keySet());
172        }
173
174}