SpringBoot JDBC 源碼分析之——NamedParameterJdbcTemplate 查詢數據返回bean對象

1,NamedParameterJdbcTemplate 查詢列表java

/***測試***/

    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 替換成 sql

源碼:getPreparedStatementCreator 方法。 因此最後仍是調用  JdbcTemplate  模板。數據庫

3,按源碼一直走下去。下面是一個抽象類實現的模板方法。app

/** * 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 類,也就是用這個類來裝載返回的數據。ide

  一開始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.this

/** * 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 。spa

/** * 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; }

總結: 由於源碼就是這樣寫死的。因此要約定裝配實體類。debug

相關文章
相關標籤/搜索