zoukankan      html  css  js  c++  java
  • SpringBoot JDBC 源码分析之——NamedParameterJdbcTemplate 查询数据返回bean对象

    1,NamedParameterJdbcTemplate 查询列表

     /***测试***/
    
        public void   queyBeanTest(){
            String s = "select * from PT_USER ";
    
            List<PtUser> list = namedJdbcTemplate.query(s, new BeanPropertyRowMapper<PtUser>(PtUser.class));
    
    
            System.out.println(list);
    
        }
    

      

    2,如果有参数,期间会把  参数绑定的 例如: :name 替换成

    源码:getPreparedStatementCreator 方法。 所以最后还是调用  JdbcTemplate  模板。

    3,按源码一直走下去。下面是一个抽象类实现的模板方法。

    /**
         * Query using a prepared statement, allowing for a PreparedStatementCreator
         * and a PreparedStatementSetter. Most other query methods use this method,
         * but application code will always work with either a creator or a setter.
         * @param psc Callback handler that can create a PreparedStatement given a
         * Connection
         * @param pss object that knows how to set values on the prepared statement.
         * If this is null, the SQL will be assumed to contain no bind parameters.
         * @param rse object that will extract results.
         * @return an arbitrary result object, as returned by the ResultSetExtractor
         * @throws DataAccessException if there is any problem
         */
        public <T> T query(
                PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
                throws DataAccessException {
    
            Assert.notNull(rse, "ResultSetExtractor must not be null");
            logger.debug("Executing prepared SQL query");
    
            return execute(psc, new PreparedStatementCallback<T>() {
                @Override
                public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
                    ResultSet rs = null;
                    try {
                        if (pss != null) {
                            pss.setValues(ps);
                        }
                        rs = ps.executeQuery();
                        ResultSet rsToUse = rs;
                        if (nativeJdbcExtractor != null) {
                            rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
                        }
                        return rse.extractData(rsToUse);
                    }
                    finally {
                        JdbcUtils.closeResultSet(rs);
                        if (pss instanceof ParameterDisposer) {
                            ((ParameterDisposer) pss).cleanupParameters();
                        }
                    }
                }
            });
        }

    4,因为一开始 我们 用的 BeanPropertyRowMapper 类,也就是用这个类来装载返回的数据。

      一开始new 的时候会初始化方法。

      

        /**
         * Create a new {@code BeanPropertyRowMapper}, accepting unpopulated
         * properties in the target bean.
         * <p>Consider using the {@link #newInstance} factory method instead,
         * which allows for specifying the mapped type once only.
         * @param mappedClass the class that each row should be mapped to
         */
        public BeanPropertyRowMapper(Class<T> mappedClass) {
            initialize(mappedClass);
        }

    这个方法,其实是把实体类的属性拆分了来存的,例如:userName 存为user_name.

        /**
         * Initialize the mapping metadata for the given class.
         * @param mappedClass the mapped class
         */
        protected void initialize(Class<T> mappedClass) {
            this.mappedClass = mappedClass;
            this.mappedFields = new HashMap<String, PropertyDescriptor>();
            this.mappedProperties = new HashSet<String>();
            PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
            for (PropertyDescriptor pd : pds) {
                if (pd.getWriteMethod() != null) {
                    this.mappedFields.put(lowerCaseName(pd.getName()), pd);
                    String underscoredName = underscoreName(pd.getName());
                    if (!lowerCaseName(pd.getName()).equals(underscoredName)) {
                        this.mappedFields.put(underscoredName, pd);
                    }
                    this.mappedProperties.add(pd.getName());
                }
            }
        }

      下面这个方法是最终实现,会把数据库的字段都转成小写。所以也支持Oracle 。

    /**
         * Extract the values for all columns in the current row.
         * <p>Utilizes public setters and result set metadata.
         * @see java.sql.ResultSetMetaData
         */
        @Override
        public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
            Assert.state(this.mappedClass != null, "Mapped class was not specified");
            T mappedObject = BeanUtils.instantiateClass(this.mappedClass);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
            initBeanWrapper(bw);
    
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            Set<String> populatedProperties = (isCheckFullyPopulated() ? new HashSet<String>() : null);
    
            for (int index = 1; index <= columnCount; index++) {
                String column = JdbcUtils.lookupColumnName(rsmd, index);
                String field = lowerCaseName(column.replaceAll(" ", ""));
                PropertyDescriptor pd = this.mappedFields.get(field);
                if (pd != null) {
                    try {
                        Object value = getColumnValue(rs, index, pd);
                        if (rowNumber == 0 && logger.isDebugEnabled()) {
                            logger.debug("Mapping column '" + column + "' to property '" + pd.getName() +
                                    "' of type '" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "'");
                        }
                        try {
                            bw.setPropertyValue(pd.getName(), value);
                        }
                        catch (TypeMismatchException ex) {
                            if (value == null && this.primitivesDefaultedForNullValue) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
                                            " and column '" + column + "' with null value when setting property '" +
                                            pd.getName() + "' of type '" +
                                            ClassUtils.getQualifiedName(pd.getPropertyType()) +
                                            "' on object: " + mappedObject, ex);
                                }
                            }
                            else {
                                throw ex;
                            }
                        }
                        if (populatedProperties != null) {
                            populatedProperties.add(pd.getName());
                        }
                    }
                    catch (NotWritablePropertyException ex) {
                        throw new DataRetrievalFailureException(
                                "Unable to map column '" + column + "' to property '" + pd.getName() + "'", ex);
                    }
                }
                else {
                    // No PropertyDescriptor found
                    if (rowNumber == 0 && logger.isDebugEnabled()) {
                        logger.debug("No property found for column '" + column + "' mapped to field '" + field + "'");
                    }
                }
            }
    
            if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
                throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " +
                        "necessary to populate object of class [" + this.mappedClass.getName() + "]: " +
                        this.mappedProperties);
            }
    
            return mappedObject;
        }

    总结: 因为源码就是这样写死的。所以要约定装配实体类。

  • 相关阅读:
    LeetCode "Palindrome Partition II"
    LeetCode "Longest Substring Without Repeating Characters"
    LeetCode "Wildcard Matching"
    LeetCode "Best Time to Buy and Sell Stock II"
    LeetCodeEPI "Best Time to Buy and Sell Stock"
    LeetCode "Substring with Concatenation of All Words"
    LeetCode "Word Break II"
    LeetCode "Word Break"
    Some thoughts..
    LeetCode "Longest Valid Parentheses"
  • 原文地址:https://www.cnblogs.com/chen-msg/p/8927295.html
Copyright © 2011-2022 走看看