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.beans.PropertyDescriptor;
020import java.util.ArrayList;
021import java.util.List;
022
023import org.springframework.beans.BeanWrapper;
024import org.springframework.beans.NotReadablePropertyException;
025import org.springframework.beans.PropertyAccessor;
026import org.springframework.beans.PropertyAccessorFactory;
027import org.springframework.jdbc.core.StatementCreatorUtils;
028import org.springframework.lang.NonNull;
029import org.springframework.lang.Nullable;
030import org.springframework.util.StringUtils;
031
032/**
033 * {@link SqlParameterSource} implementation that obtains parameter values
034 * from bean properties of a given JavaBean object. The names of the bean
035 * properties have to match the parameter names.
036 *
037 * <p>Uses a Spring BeanWrapper for bean property access underneath.
038 *
039 * @author Thomas Risberg
040 * @author Juergen Hoeller
041 * @since 2.0
042 * @see NamedParameterJdbcTemplate
043 * @see org.springframework.beans.BeanWrapper
044 */
045public class BeanPropertySqlParameterSource extends AbstractSqlParameterSource {
046
047        private final BeanWrapper beanWrapper;
048
049        @Nullable
050        private String[] propertyNames;
051
052
053        /**
054         * Create a new BeanPropertySqlParameterSource for the given bean.
055         * @param object the bean instance to wrap
056         */
057        public BeanPropertySqlParameterSource(Object object) {
058                this.beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(object);
059        }
060
061
062        @Override
063        public boolean hasValue(String paramName) {
064                return this.beanWrapper.isReadableProperty(paramName);
065        }
066
067        @Override
068        @Nullable
069        public Object getValue(String paramName) throws IllegalArgumentException {
070                try {
071                        return this.beanWrapper.getPropertyValue(paramName);
072                }
073                catch (NotReadablePropertyException ex) {
074                        throw new IllegalArgumentException(ex.getMessage());
075                }
076        }
077
078        /**
079         * Derives a default SQL type from the corresponding property type.
080         * @see org.springframework.jdbc.core.StatementCreatorUtils#javaTypeToSqlParameterType
081         */
082        @Override
083        public int getSqlType(String paramName) {
084                int sqlType = super.getSqlType(paramName);
085                if (sqlType != TYPE_UNKNOWN) {
086                        return sqlType;
087                }
088                Class<?> propType = this.beanWrapper.getPropertyType(paramName);
089                return StatementCreatorUtils.javaTypeToSqlParameterType(propType);
090        }
091
092        @Override
093        @NonNull
094        public String[] getParameterNames() {
095                return getReadablePropertyNames();
096        }
097
098        /**
099         * Provide access to the property names of the wrapped bean.
100         * Uses support provided in the {@link PropertyAccessor} interface.
101         * @return an array containing all the known property names
102         */
103        public String[] getReadablePropertyNames() {
104                if (this.propertyNames == null) {
105                        List<String> names = new ArrayList<>();
106                        PropertyDescriptor[] props = this.beanWrapper.getPropertyDescriptors();
107                        for (PropertyDescriptor pd : props) {
108                                if (this.beanWrapper.isReadableProperty(pd.getName())) {
109                                        names.add(pd.getName());
110                                }
111                        }
112                        this.propertyNames = StringUtils.toStringArray(names);
113                }
114                return this.propertyNames;
115        }
116
117}