JdbcTemplate源码解析之query操作

本文深入解析JdbcTemplate的query方法,从构造JdbcTemplate对象到执行查询,详细分析了源码流程,特别是BeanPropertyRowMapper如何将查询结果映射到Bean对象上。通过内省机制,实现了将数据库数据封装到实体类的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


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

JdbcTemplate.java

[Java] 纯文本查看 复制代码
?
1
2
3
4
public JdbcTemplate(DataSource dataSource) {
    setDataSource(dataSource);
    afterPropertiesSet();
}


JdbcAccessor.java

[Java] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
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] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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] 纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
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的过程.当然,这块因为篇幅的原因可能有点粗糙,但是主要起到一个抛砖引玉的作用~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值