黑马程序员技术交流社区
标题: 【济南校区】JdbcTemplate源码解析之query操作 [打印本页]
作者: 大山哥哥 时间: 2019-2-28 20:35
标题: 【济南校区】JdbcTemplate源码解析之query操作
咱们上次对执行增删改的操作进行分析以后,这次则来对查询进行流程梳理把~
源码解析
接下来则来对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的过程.当然,这块因为篇幅的原因可能有点粗糙,但是主要起到一个抛砖引玉的作用~~~
作者: JustJavac 时间: 2019-5-30 10:09
贤哥,我赞完变成NaN了...js有问题,刷新才加1....
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |