A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

咱们上次对执行增删改的操作进行分析以后,这次则来对查询进行流程梳理把~
源码解析


接下来则来对JdbcTemplate的query方法进行一个源码解析吧~
eg:
    List<User> users = template.query(sql,new BeanPropertyRowMapper<User>(User.class),params.toArray())
这里我们来分析下为什么返回值是List<User>
第一步是创建JdbcTemplate对象,传入数据源,进入构造方法查看

JdbcTemplate.java
[Java] 纯文本查看 复制代码
    public JdbcTemplate(DataSource dataSource) {
        setDataSource(dataSource);
        afterPropertiesSet();
    }

JdbcAccessor.java
[Java] 纯文本查看 复制代码
   private DataSource dataSource;
    private boolean lazyInit = true;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    public DataSource getDataSource() {
        return this.dataSource;
    }
    public boolean isLazyInit() {
        return this.lazyInit;
    }
    public void afterPropertiesSet() {
        if (getDataSource() == null) {
            throw new IllegalArgumentException("Property 'dataSource' is required");
        }
        if (!isLazyInit()) {
            getExceptionTranslator();
        }
    }

通过上面代码来看说白了就是用来初始化我们JdbcTemplate对象的,当然也做了不少操作,比如要判断是够有提供dataSource对象

第二步是调用对应的query方法,把sql和参数传入最后接收响应结果


创建完我们的jdbcTemplate对象后那么接下来才真正的进入到我们执行查询的query方法中~
JdbcTemplate.java
[Java] 纯文本查看 复制代码
    public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
		//---------
		//标注五
        return (List)this.query((String)sql, (Object[])args, (ResultSetExtractor)(new RowMapperResultSetExtractor(rowMapper)));
    }

	...

    public <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
        return this.query(sql, this.newArgPreparedStatementSetter(args), rse);
    }
	
	...

    public <T> T query(String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
        return this.query((PreparedStatementCreator)(new JdbcTemplate.SimplePreparedStatementCreator(sql)), (PreparedStatementSetter)pss, (ResultSetExtractor)rse);
    }
	
	...

	//---------
	//标注一
    public <T> T query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse) throws DataAccessException {
        Assert.notNull(rse, "ResultSetExtractor must not be null");
        this.logger.debug("Executing prepared SQL query");
		//---------
		//标注二
        return this.execute(psc, new PreparedStatementCallback<T>() {
            public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
				//---------
				//标注三	
                ResultSet rs = null;

                Object var4;
                try {
                    if (pss != null) {
						//将预编译的PreparedStatement对象和sql语句的?占位符和实际参数进行绑定
                        pss.setValues(ps);
                    }
					//获取结果集
                    rs = ps.executeQuery();
                    ResultSet rsToUse = rs;
                    if (JdbcTemplate.this.nativeJdbcExtractor != null) {
                        rsToUse = JdbcTemplate.this.nativeJdbcExtractor.getNativeResultSet(rs);
                    }
					//---------
					//标注四
                    var4 = rse.extractData(rsToUse);
                } finally {
                    JdbcUtils.closeResultSet(rs);
                    if (pss instanceof ParameterDisposer) {
                        ((ParameterDisposer)pss).cleanupParameters();
                    }

                }

                return var4;
            }
        });
    }	

通过上面代码来看,我们很容易就能够追到标注一的地方,而这里也是触发查询操作的开始,接下来咱们则来对这个方法进行一个详细的剖析,看到标注二这里相信大家就能感到很眼熟了,因为上次我们在追update方法的源码时最终也会执行到execute方法,在这我们就直接将execute方法执行原理不在复述一遍了.我们直接从标注三开始,因为doInPreparedStatement方法的返回值其实就是最终的返回值

标注四
首先先来看下rse对象
    ResultSetExtractor<T> rse
这个对象是在标注五的地方进行的初始化,(ResultSetExtractor)(new RowMapperResultSetExtractor(rowMapper)),通过这段代码我们可以看到我们将RowMapperResultSetExtractor对象强制转换成ResultSetExtractor,但是创建RowMapperResultSetExtractor对象的时候我们传入了一个rowMapper对象,这个对象,则是咱们查询的时候传入的BeanPropertyRowMapper,接下来咱们从RowMapperResultSetExtractor开始分析

标注五
RowMapperResultSetExtractor
[Java] 纯文本查看 复制代码
	public class RowMapperResultSetExtractor<T> implements ResultSetExtractor<List<T>> {
	    private final RowMapper<T> rowMapper;
	    private final int rowsExpected;
	
	    public RowMapperResultSetExtractor(RowMapper<T> rowMapper) {
	        this(rowMapper, 0);
	    }
	
	    public RowMapperResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {
	        Assert.notNull(rowMapper, "RowMapper is required");
	        this.rowMapper = rowMapper;
	        this.rowsExpected = rowsExpected;
	    }
		//---------
		//标注六
	    public List<T> extractData(ResultSet rs) throws SQLException {
	        List<T> results = this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList();
	        int var3 = 0;
	
	        while(rs.next()) {
	            results.add(this.rowMapper.mapRow(rs, var3++));
	        }
	
	        return results;
	    }
	}

通过上面的代码我们看到该类中保存了RowMapper和rowsExpected,因为走的是一个参数的构造所以rowsExpected为0,因此标注4中的rse对象的实际对象为RowMapperResultSetExtractor,而rse.extractData(rsToUse);方法则进入

标注六
    List<T> results = this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList();
经过之前的分析,rowsExpected默认为0,所以执行三元表达式的后半部分,创建一个新的ArrayList
    while(rs.next()) {
        //---------        
        //标注7
        results.add(this.rowMapper.mapRow(rs, var3++));
    }
这段代码中则是将结果集的每条结果添加到目标ArrayList中,因此我们又把注意力转向了this.rowMapper.mapRow(rs, var3++)

标注七
rowMapper这个对象实际类型为BeanPropertyRowMapper
BeanPropertyRowMapper初始化走的构造
[Java] 纯文本查看 复制代码
    public BeanPropertyRowMapper(Class<T> mappedClass) {
        this.initialize(mappedClass);
    }
    protected void initialize(Class<T> mappedClass) {
        this.mappedClass = mappedClass;
        this.mappedFields = new HashMap();
        this.mappedProperties = new HashSet();
        PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
        PropertyDescriptor[] var3 = pds;
        int var4 = pds.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            PropertyDescriptor pd = var3[var5];
            if (pd.getWriteMethod() != null) {
                this.mappedFields.put(this.lowerCaseName(pd.getName()), pd);
                String underscoredName = this.underscoreName(pd.getName());
                if (!this.lowerCaseName(pd.getName()).equals(underscoredName)) {
                    this.mappedFields.put(underscoredName, pd);
                }

                this.mappedProperties.add(pd.getName());
            }
        }

    }

上面用到了内省,这块简单介绍下,就是通过内省我们可以轻松获取到一个类的属性和相关get/set方法,只要属性和相关方法对象,那么我们就可以通过反射的形式将结果集中的数据封装到该对象中,而上面代码只是进行了参数的初始化方便mapRow方法进行封装而已
[Java] 纯文本查看 复制代码
    public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
        Assert.state(this.mappedClass != null, "Mapped class was not specified");
		//---------	
		//标注八:初始化目标对象
        T mappedObject = BeanUtils.instantiate(this.mappedClass);
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
        this.initBeanWrapper(bw);
		//结果集元数据
        ResultSetMetaData rsmd = rs.getMetaData();
		//获取一共多少列
        int columnCount = rsmd.getColumnCount();
        Set<String> populatedProperties = this.isCheckFullyPopulated() ? new HashSet() : null;
		//---------	
		//标注九:初始化目标对象
		//循环遍历每一列,将每列的值封装到实体bean的指定属性中
        for(int index = 1; index <= columnCount; ++index) {
            String column = JdbcUtils.lookupColumnName(rsmd, index);
            String field = this.lowerCaseName(column.replaceAll(" ", ""));
            PropertyDescriptor pd = (PropertyDescriptor)this.mappedFields.get(field);
            if (pd == null) {
                if (rowNumber == 0 && this.logger.isDebugEnabled()) {
                    this.logger.debug("No property found for column '" + column + "' mapped to field '" + field + "'");
                }
            } else {
                try {
					//获取该列的数据,同时找到判断具体数据类型
                    Object value = this.getColumnValue(rs, index, pd);
                    if (rowNumber == 0 && this.logger.isDebugEnabled()) {
                        this.logger.debug("Mapping column '" + column + "' to property '" + pd.getName() + "' of type [" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "]");
                    }

                    try {
						//将Bean的属性和获取到的值进行绑定
                        bw.setPropertyValue(pd.getName(), value);
                    } catch (TypeMismatchException var14) {
                        if (value != null || !this.primitivesDefaultedForNullValue) {
                            throw var14;
                        }

                        if (this.logger.isDebugEnabled()) {
                            this.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, var14);
                        }
                    }

                    if (populatedProperties != null) {
                        populatedProperties.add(pd.getName());
                    }
                } catch (NotWritablePropertyException var15) {
                    throw new DataRetrievalFailureException("Unable to map column '" + column + "' to property '" + pd.getName() + "'", var15);
                }
            }
        }

        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);
        } else {
            return mappedObject;
        }
    }

标注八
[Java] 纯文本查看 复制代码
    public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException {
        Assert.notNull(clazz, "Class must not be null");
        if (clazz.isInterface()) {
            throw new BeanInstantiationException(clazz, "Specified class is an interface");
        } else {
            try {
                return clazz.newInstance();
            } catch (InstantiationException var2) {
                throw new BeanInstantiationException(clazz, "Is it an abstract class?", var2);
            } catch (IllegalAccessException var3) {
                throw new BeanInstantiationException(clazz, "Is the constructor accessible?", var3);
            }
        }
    }

ok,到此为止我们就能分析出来,在这段for循环中才是将实体Bean对象中的属性值和rs中每列的数据进行绑定,从而完成将查询到的数据封装到Bean的过程.当然,这块因为篇幅的原因可能有点粗糙,但是主要起到一个抛砖引玉的作用~~~

1 个回复

正序浏览
贤哥,我赞完变成NaN了...js有问题,刷新才加1....
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马