读完本篇文章需要很长很长时间。。。。

传统JDBC

相信大家对传统的jdbc已经很熟悉了,无非就是下面这个流程

1
2
3
4
5
6
7
8
9
10
11
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2. 获得数据库连接
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
//3.操作数据库
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name, age from user");
//4.处理返回值
while(rs.next()){
System.out.println("名字:"+rs.getString("name")+" 年龄:"+rs.getInt("age"));
}

使用SpringJDBC

引入maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>

实体类

1
2
3
4
5
6
public class User {
private int id;
private String name;
private int age;
//getter、setter省略
}

返回值处理类

1
2
3
4
5
6
7
8
9
10
public class UserRowMapper implements RowMapper {
@Nullable
public Object mapRow(ResultSet resultSet, int i) throws SQLException {
User user=new User();
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
user.setAge(resultSet.getInt("age"));
return user;
}
}

业务处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface JDBCService {

public void queryById(int id);
public void updateNameById(int id,String name);
}
public class JDBCServiceImpl implements JDBCService {
private JdbcTemplate jdbcTemplate;

public JDBCServiceImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public void queryById(int id) {
List<User> list = jdbcTemplate.query("select id,name,age from user where id=?", new Object[]{id}, new UserRowMapper());
if (list.size() > 0) {
System.out.println("id 为" + id + "的用户名为:" + list.get(0).getName());
}
}

public void updateNameById(int id, String name) {
jdbcTemplate.update("update user set name=? where id=?", new Object[]{name, id}, new UserRowMapper());
}
}

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class JDBCConfig {
@Bean
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource=new DruidDataSource();
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://172.16.40.159:3306/cfkk?characterEncoding=utf-8&useSSL=false");
return druidDataSource;
}
@Bean
public JDBCService jdbcService(DruidDataSource druidDataSource){
JdbcTemplate jdbcTemplate=new JdbcTemplate(druidDataSource);
JDBCService jdbcService=new JDBCServiceImpl(jdbcTemplate);
return jdbcService;
}
}

启动类

1
2
3
4
5
6
7
8
public class JDBCDemo {
public static void main (String args[]){
ApplicationContext context = new AnnotationConfigApplicationContext("cn.shiyujun.config");
JDBCService jdbcService= context.getBean(JDBCService.class);
jdbcService.updateNameById(1,"李四");
jdbcService.queryById(1);
}
}

至此Deno工程搭建完毕,有需要源码的同学可以从下方地址获取

https://github.com/shiyujun/spring-framework

update

我们首先来分析一下update方法的实现

参数封装
1
2
3
4
5
6
7
public int update(String sql, @Nullable Object... args) throws DataAccessException {
return update(sql, newArgPreparedStatementSetter(args));
}

public int update(String sql, @Nullable PreparedStatementSetter pss) throws DataAccessException {
return update(new SimplePreparedStatementCreator(sql), pss);
}

可以看到,首先使用了ArgPreparedStatementSetter对参数进行了一层封装,然后又使用了SimplePreparedStatementCreator对SQL进行了封装

核心逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)
throws DataAccessException {

logger.debug("Executing prepared SQL update");

return updateCount(execute(psc, ps -> {
try {
if (pss != null) {
//设置所需的全部参数
pss.setValues(ps);
}
//调用jdbc的更新方法
int rows = ps.executeUpdate();
if (logger.isDebugEnabled()) {
logger.debug("SQL update affected " + rows + " rows");
}
return rows;
}
finally {
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}));
}

上方这个方法是update的核心逻辑了,这里面的代码不是太多,主要涉及几个核心的逻辑

获取更新条数updateCount

进入方法以后可以看到这个方法是对execute方法的integer返回值的一个封装,也就是此次修改的行数

1
2
3
4
private static int updateCount(@Nullable Integer result) { 
Assert.state(result != null, "No update count");
return result;
}
前置方法execute

这里完成了传统JDBC的前两步加载驱动和获取连接,

1
2
3
4
5
6
7
8
9
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
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
throws DataAccessException {

Assert.notNull(psc, "PreparedStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (logger.isDebugEnabled()) {
String sql = getSql(psc);
logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
}
//根据具体的连接池组件获取数据库连接,这里就不深入研究了,放到以后的连接池源码解析里
Connection con = DataSourceUtils.getConnection(obtainDataSource());
PreparedStatement ps = null;
try {
//应该对这个PreparedStatement印象很深刻了
ps = psc.createPreparedStatement(con);
applyStatementSettings(ps);
//调用回调函数也就是update方法中execute的lambda表达式里的逻辑
T result = action.doInPreparedStatement(ps);
//警告处理
handleWarnings(ps);
return result;
}
catch (SQLException ex) {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
String sql = getSql(psc);
// 释放资源
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("PreparedStatementCallback", sql, ex);
}
finally {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
执行更新

现在就进入了最后的逻辑了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)
throws DataAccessException {

logger.debug("Executing prepared SQL update");

return updateCount(execute(psc, ps -> {
try {
if (pss != null) {
// 往下看
pss.setValues(ps);
}
//调用jdbc的方法执行更新
int rows = ps.executeUpdate();
if (logger.isDebugEnabled()) {
logger.debug("SQL update affected " + rows + " rows");
}
return rows;
}
finally {
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}));
}
请求参数设置
1
2
3
4
5
6
7
8
9
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
public void setValues(PreparedStatement ps) throws SQLException {
int parameterPosition = 1;
if (this.args != null && this.argTypes != null) {
//遍历参数
for (int i = 0; i < this.args.length; i++) {
Object arg = this.args[i];
//如果是集合的话则递归处理
if (arg instanceof Collection && this.argTypes[i] != Types.ARRAY) {
Collection<?> entries = (Collection<?>) arg;
for (Object entry : entries) {
if (entry instanceof Object[]) {
Object[] valueArray = ((Object[]) entry);
for (Object argValue : valueArray) {
doSetValue(ps, parameterPosition, this.argTypes[i], argValue);
parameterPosition++;
}
}
else {
doSetValue(ps, parameterPosition, this.argTypes[i], entry);
parameterPosition++;
}
}
}
else {
//核心方法
doSetValue(ps, parameterPosition, this.argTypes[i], arg);
parameterPosition++;
}
}
}
}
protected void doSetValue(PreparedStatement ps, int parameterPosition, int argType, Object argValue)
throws SQLException {

StatementCreatorUtils.setParameterValue(ps, parameterPosition, argType, argValue);
}

public static void setParameterValue(PreparedStatement ps, int paramIndex, int sqlType,
@Nullable Object inValue) throws SQLException {

setParameterValueInternal(ps, paramIndex, sqlType, null, null, inValue);
}
private static void setParameterValueInternal(PreparedStatement ps, int paramIndex, int sqlType,
@Nullable String typeName, @Nullable Integer scale, @Nullable Object inValue) throws SQLException {

String typeNameToUse = typeName;
int sqlTypeToUse = sqlType;
Object inValueToUse = inValue;

// override type info?
if (inValue instanceof SqlParameterValue) {
SqlParameterValue parameterValue = (SqlParameterValue) inValue;
if (logger.isDebugEnabled()) {
logger.debug("Overriding type info with runtime info from SqlParameterValue: column index " + paramIndex +
", SQL type " + parameterValue.getSqlType() + ", type name " + parameterValue.getTypeName());
}
if (parameterValue.getSqlType() != SqlTypeValue.TYPE_UNKNOWN) {
sqlTypeToUse = parameterValue.getSqlType();
}
if (parameterValue.getTypeName() != null) {
typeNameToUse = parameterValue.getTypeName();
}
inValueToUse = parameterValue.getValue();
}

if (logger.isTraceEnabled()) {
logger.trace("Setting SQL statement parameter value: column index " + paramIndex +
", parameter value [" + inValueToUse +
"], value class [" + (inValueToUse != null ? inValueToUse.getClass().getName() : "null") +
"], SQL type " + (sqlTypeToUse == SqlTypeValue.TYPE_UNKNOWN ? "unknown" : Integer.toString(sqlTypeToUse)));
}

if (inValueToUse == null) {
setNull(ps, paramIndex, sqlTypeToUse, typeNameToUse);
}
else {
//往下看
setValue(ps, paramIndex, sqlTypeToUse, typeNameToUse, scale, inValueToUse);
}
}

private static void setValue(PreparedStatement ps, int paramIndex, int sqlType,
@Nullable String typeName, @Nullable Integer scale, Object inValue) throws SQLException {

if (inValue instanceof SqlTypeValue) {
((SqlTypeValue) inValue).setTypeValue(ps, paramIndex, sqlType, typeName);
}
else if (inValue instanceof SqlValue) {
((SqlValue) inValue).setValue(ps, paramIndex);
}
else if (sqlType == Types.VARCHAR || sqlType == Types.NVARCHAR ||
sqlType == Types.LONGVARCHAR || sqlType == Types.LONGNVARCHAR) {
ps.setString(paramIndex, inValue.toString());
}
else if ((sqlType == Types.CLOB || sqlType == Types.NCLOB) && isStringValue(inValue.getClass())) {
String strVal = inValue.toString();
if (strVal.length() > 4000) {
// Necessary for older Oracle drivers, in particular when running against an Oracle 10 database.
// Should also work fine against other drivers/databases since it uses standard JDBC 4.0 API.
if (sqlType == Types.NCLOB) {
ps.setNClob(paramIndex, new StringReader(strVal), strVal.length());
}
else {
ps.setClob(paramIndex, new StringReader(strVal), strVal.length());
}
return;
}
// Fallback: regular setString binding
ps.setString(paramIndex, strVal);
}
else if (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC) {
if (inValue instanceof BigDecimal) {
ps.setBigDecimal(paramIndex, (BigDecimal) inValue);
}
else if (scale != null) {
ps.setObject(paramIndex, inValue, sqlType, scale);
}
else {
ps.setObject(paramIndex, inValue, sqlType);
}
}
else if (sqlType == Types.BOOLEAN) {
if (inValue instanceof Boolean) {
ps.setBoolean(paramIndex, (Boolean) inValue);
}
else {
ps.setObject(paramIndex, inValue, Types.BOOLEAN);
}
}
else if (sqlType == Types.DATE) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Date) {
ps.setDate(paramIndex, (java.sql.Date) inValue);
}
else {
ps.setDate(paramIndex, new java.sql.Date(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setDate(paramIndex, new java.sql.Date(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.DATE);
}
}
else if (sqlType == Types.TIME) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Time) {
ps.setTime(paramIndex, (java.sql.Time) inValue);
}
else {
ps.setTime(paramIndex, new java.sql.Time(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTime(paramIndex, new java.sql.Time(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.TIME);
}
}
else if (sqlType == Types.TIMESTAMP) {
if (inValue instanceof java.util.Date) {
if (inValue instanceof java.sql.Timestamp) {
ps.setTimestamp(paramIndex, (java.sql.Timestamp) inValue);
}
else {
ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
}
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
}
else {
ps.setObject(paramIndex, inValue, Types.TIMESTAMP);
}
}
else if (sqlType == SqlTypeValue.TYPE_UNKNOWN || (sqlType == Types.OTHER &&
"Oracle".equals(ps.getConnection().getMetaData().getDatabaseProductName()))) {
if (isStringValue(inValue.getClass())) {
ps.setString(paramIndex, inValue.toString());
}
else if (isDateValue(inValue.getClass())) {
ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
}
else if (inValue instanceof Calendar) {
Calendar cal = (Calendar) inValue;
ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
}
else {
// Fall back to generic setObject call without SQL type specified.
ps.setObject(paramIndex, inValue);
}
}
else {
// Fall back to generic setObject call with SQL type specified.
ps.setObject(paramIndex, inValue, sqlType);
}
}

至此update方法更新分析完毕

query

1
2
3
4
5
public <T> List<T> query(String sql, @Nullable Object[] args, 
RowMapper<T> rowMapper) throws DataAccessException {
return result(query(sql, args, new
RowMapperResultSetExtractor<>(rowMapper)));
}

首先最外层的result方法没有什么特别的,就是对返回值的处理

1
2
3
4
private static <T> T result(@Nullable T result) {
Assert.state(result != null, "No result");
return result;
}
参数封装

接着同样是封装请求参数

1
2
3
4
5
6
public <T> T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
return query(sql, newArgPreparedStatementSetter(args), rse);
}
public <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
return query(new SimplePreparedStatementCreator(sql), pss, rse);
}
核心查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public <T> T query(
PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException {

Assert.notNull(rse, "ResultSetExtractor must not be null");
logger.debug("Executing prepared SQL query");
//这里的execute的逻辑与update是一样的
return execute(psc, new PreparedStatementCallback<T>() {
@Override
@Nullable
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
if (pss != null) {
// 同上
pss.setValues(ps);
}
rs = ps.executeQuery();
//这里是重点
return rse.extractData(rs);
}
finally {
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}
处理返回值

因为修改的时候只需要返回一个int值修改的条数就ok了,但是查询的时候各种查询的类型什么的就不一样了。所以在这需要单独处理一下

1
2
3
4
5
6
7
8
9
10
@Override
public List<T> extractData(ResultSet rs) throws SQLException {
List<T> results = (this.rowsExpected > 0 ? new ArrayList<>(this.rowsExpected) : new ArrayList<>());
int rowNum = 0;
while (rs.next()) {
//在这里就是调用的我们一开始定义的UserRowMapper的mapRow方法
results.add(this.rowMapper.mapRow(rs, rowNum++));
}
return results;
}

SpringJDBC源码解析的更多相关文章

  1. Spring源码解析系列汇总

    相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的Spring源码解析系列文章的汇总,总共包含以下专题.喜欢的同学可以收藏起来以备不时之需 SpringIOC源码解析(上) 本篇文章搭建了IOC源 ...

  2. SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis

    本篇我们在SpringBoot中整合Mybatis这个orm框架,毕竟分析一下其自动配置的源码,我们先来回顾一下以前Spring中是如何整合Mybatis的,大家可以看看我这篇文章Mybaits 源码 ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  6. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  7. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  8. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  9. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

随机推荐

  1. (八)OpenStack---M版---双节点搭建---Cinder安装和配置

    ↓↓↓↓↓↓↓↓视频已上线B站↓↓↓↓↓↓↓↓ >>>>>>传送门 1.创建数据库并授权 2.获得admin凭证执行管理员命令并创建服务证书 3.创建块存储设备AP ...

  2. Windows隐藏用户小技巧

    0x01 前言 本文目的在于对于Windows账户访问与控制的技术理论进行分析与总结,熟悉了解Windows账户的特性及相关操作,并依此进行Windows账户的相关实验,进行实验记录与总结.通过本文记 ...

  3. 使用map将字数组里的对象重新组装

    变为数组  ["扬子","北京","上海海吉雅"] // 注意点 map循环的时候 不能够有空的 否则回出问题哦. var list= [{ ...

  4. py2,py3区别 ,基础网络常识,多线程,

    1.编译型:把代码编译成机器码,计算机找虚拟机执行代码,机器码交给计算技巧去运行 :C语言,java c# 解释型:边解释边执行 5.只有列表才能reverse 字符串需要索引[::-1] 6.py2 ...

  5. 使用设备树来编写led驱动程序

    在总线设备驱动模型中,平台设备是写在c文件中.使用设备树时,平台设备事先并不存在,在dts文件中构造节点,节点里面含有资源.dts文件被编译成dtb文件,然后传递给内核.内核会解析dtb文件,得到一个 ...

  6. 1. jenkins 安装部署

    参考文档: https://jenkins.io/zh 准备环境 - jdk          路径: https://www.oracle.com/technetwork/java/javase/a ...

  7. 4-ESP8266 SDK开发基础入门篇--串口

    所有的源码 https://gitee.com/yang456/Learn8266SDKDevelop.git 手头有任何8266的板子就可以,不需要购买 https://item.taobao.co ...

  8. [LeetCode] 21. Merge Two Sorted Lists 混合插入有序链表

    Merge two sorted linked lists and return it as a new list. The new list should be made by splicing t ...

  9. AOP小记

    编程思想演进:POP(Procedure Oriented Programming)-> OOP(Object Oriented Programming)-> AOP(Aspect Ori ...

  10. [LeetCode] 321. Create Maximum Number 创建最大数

    Given two arrays of length m and n with digits 0-9 representing two numbers. Create the maximum numb ...