文章结构

  1. 传统JDBC的使用
  2. 源码阅读环境的搭建
  3. 使用JdbcTemplate以update方法切入
  4. 获取连接加载驱动等方法的封装
  5. 请求参数的封装
  6. 调用驱动提供的方法
  7. query方法的分析,与update仅有返回结果的处理不同其他完全相同

  8. 传统JDBC

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

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

使用SpringJDBC

引入maven依赖

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

实体类

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

返回值处理类

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

业务处理类

  1. public interface JDBCService {
  2. public void queryById(int id);
  3. public void updateNameById(int id,String name);
  4. }
  5. public class JDBCServiceImpl implements JDBCService {
  6. private JdbcTemplate jdbcTemplate;
  7. public JDBCServiceImpl(JdbcTemplate jdbcTemplate) {
  8. this.jdbcTemplate = jdbcTemplate;
  9. }
  10. public void queryById(int id) {
  11. List<User> list = jdbcTemplate.query("select id,name,age from user where id=?", new Object[]{id}, new UserRowMapper());
  12. if (list.size() > 0) {
  13. System.out.println("id 为" + id + "的用户名为:" + list.get(0).getName());
  14. }
  15. }
  16. public void updateNameById(int id, String name) {
  17. jdbcTemplate.update("update user set name=? where id=?", new Object[]{name, id}, new UserRowMapper());
  18. }
  19. }

配置类

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

启动类

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

至此Deno工程搭建完毕,有需要源码的同学可以从下方地址获取
https://github.com/shiyujun/spring-framework

update

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

参数封装

  1. public int update(String sql, @Nullable Object... args) throws DataAccessException {
  2. return update(sql, newArgPreparedStatementSetter(args));
  3. }
  4. public int update(String sql, @Nullable PreparedStatementSetter pss) throws DataAccessException {
  5. return update(new SimplePreparedStatementCreator(sql), pss);
  6. }

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

核心逻辑

  1. protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)
  2. throws DataAccessException {
  3. logger.debug("Executing prepared SQL update");
  4. return updateCount(execute(psc, ps -> {
  5. try {
  6. if (pss != null) {
  7. //设置所需的全部参数
  8. pss.setValues(ps);
  9. }
  10. //调用jdbc的更新方法
  11. int rows = ps.executeUpdate();
  12. if (logger.isDebugEnabled()) {
  13. logger.debug("SQL update affected " + rows + " rows");
  14. }
  15. return rows;
  16. }
  17. finally {
  18. if (pss instanceof ParameterDisposer) {
  19. ((ParameterDisposer) pss).cleanupParameters();
  20. }
  21. }
  22. }));
  23. }

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

获取更新条数updateCount

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

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

前置方法execute

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

  1. public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
  2. throws DataAccessException {
  3. Assert.notNull(psc, "PreparedStatementCreator must not be null");
  4. Assert.notNull(action, "Callback object must not be null");
  5. if (logger.isDebugEnabled()) {
  6. String sql = getSql(psc);
  7. logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
  8. }
  9. //根据具体的连接池组件获取数据库连接,这里就不深入研究了,放到以后的连接池源码解析里
  10. Connection con = DataSourceUtils.getConnection(obtainDataSource());
  11. PreparedStatement ps = null;
  12. try {
  13. //应该对这个PreparedStatement印象很深刻了
  14. ps = psc.createPreparedStatement(con);
  15. applyStatementSettings(ps);
  16. //调用回调函数也就是update方法中execute的lambda表达式里的逻辑
  17. T result = action.doInPreparedStatement(ps);
  18. //警告处理
  19. handleWarnings(ps);
  20. return result;
  21. }
  22. catch (SQLException ex) {
  23. if (psc instanceof ParameterDisposer) {
  24. ((ParameterDisposer) psc).cleanupParameters();
  25. }
  26. String sql = getSql(psc);
  27. // 释放资源
  28. JdbcUtils.closeStatement(ps);
  29. ps = null;
  30. DataSourceUtils.releaseConnection(con, getDataSource());
  31. con = null;
  32. throw translateException("PreparedStatementCallback", sql, ex);
  33. }
  34. finally {
  35. if (psc instanceof ParameterDisposer) {
  36. ((ParameterDisposer) psc).cleanupParameters();
  37. }
  38. JdbcUtils.closeStatement(ps);
  39. DataSourceUtils.releaseConnection(con, getDataSource());
  40. }
  41. }

执行更新

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

  1. protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)
  2. throws DataAccessException {
  3. logger.debug("Executing prepared SQL update");
  4. return updateCount(execute(psc, ps -> {
  5. try {
  6. if (pss != null) {
  7. // 往下看
  8. pss.setValues(ps);
  9. }
  10. //调用jdbc的方法执行更新
  11. int rows = ps.executeUpdate();
  12. if (logger.isDebugEnabled()) {
  13. logger.debug("SQL update affected " + rows + " rows");
  14. }
  15. return rows;
  16. }
  17. finally {
  18. if (pss instanceof ParameterDisposer) {
  19. ((ParameterDisposer) pss).cleanupParameters();
  20. }
  21. }
  22. }));
  23. }

请求参数设置

  1. public void setValues(PreparedStatement ps) throws SQLException {
  2. int parameterPosition = 1;
  3. if (this.args != null && this.argTypes != null) {
  4. //遍历参数
  5. for (int i = 0; i < this.args.length; i++) {
  6. Object arg = this.args[i];
  7. //如果是集合的话则递归处理
  8. if (arg instanceof Collection && this.argTypes[i] != Types.ARRAY) {
  9. Collection<?> entries = (Collection<?>) arg;
  10. for (Object entry : entries) {
  11. if (entry instanceof Object[]) {
  12. Object[] valueArray = ((Object[]) entry);
  13. for (Object argValue : valueArray) {
  14. doSetValue(ps, parameterPosition, this.argTypes[i], argValue);
  15. parameterPosition++;
  16. }
  17. }
  18. else {
  19. doSetValue(ps, parameterPosition, this.argTypes[i], entry);
  20. parameterPosition++;
  21. }
  22. }
  23. }
  24. else {
  25. //核心方法
  26. doSetValue(ps, parameterPosition, this.argTypes[i], arg);
  27. parameterPosition++;
  28. }
  29. }
  30. }
  31. }
  32. protected void doSetValue(PreparedStatement ps, int parameterPosition, int argType, Object argValue)
  33. throws SQLException {
  34. StatementCreatorUtils.setParameterValue(ps, parameterPosition, argType, argValue);
  35. }
  36. public static void setParameterValue(PreparedStatement ps, int paramIndex, int sqlType,
  37. @Nullable Object inValue) throws SQLException {
  38. setParameterValueInternal(ps, paramIndex, sqlType, null, null, inValue);
  39. }
  40. private static void setParameterValueInternal(PreparedStatement ps, int paramIndex, int sqlType,
  41. @Nullable String typeName, @Nullable Integer scale, @Nullable Object inValue) throws SQLException {
  42. String typeNameToUse = typeName;
  43. int sqlTypeToUse = sqlType;
  44. Object inValueToUse = inValue;
  45. // override type info?
  46. if (inValue instanceof SqlParameterValue) {
  47. SqlParameterValue parameterValue = (SqlParameterValue) inValue;
  48. if (logger.isDebugEnabled()) {
  49. logger.debug("Overriding type info with runtime info from SqlParameterValue: column index " + paramIndex +
  50. ", SQL type " + parameterValue.getSqlType() + ", type name " + parameterValue.getTypeName());
  51. }
  52. if (parameterValue.getSqlType() != SqlTypeValue.TYPE_UNKNOWN) {
  53. sqlTypeToUse = parameterValue.getSqlType();
  54. }
  55. if (parameterValue.getTypeName() != null) {
  56. typeNameToUse = parameterValue.getTypeName();
  57. }
  58. inValueToUse = parameterValue.getValue();
  59. }
  60. if (logger.isTraceEnabled()) {
  61. logger.trace("Setting SQL statement parameter value: column index " + paramIndex +
  62. ", parameter value [" + inValueToUse +
  63. "], value class [" + (inValueToUse != null ? inValueToUse.getClass().getName() : "null") +
  64. "], SQL type " + (sqlTypeToUse == SqlTypeValue.TYPE_UNKNOWN ? "unknown" : Integer.toString(sqlTypeToUse)));
  65. }
  66. if (inValueToUse == null) {
  67. setNull(ps, paramIndex, sqlTypeToUse, typeNameToUse);
  68. }
  69. else {
  70. //往下看
  71. setValue(ps, paramIndex, sqlTypeToUse, typeNameToUse, scale, inValueToUse);
  72. }
  73. }
  74. private static void setValue(PreparedStatement ps, int paramIndex, int sqlType,
  75. @Nullable String typeName, @Nullable Integer scale, Object inValue) throws SQLException {
  76. if (inValue instanceof SqlTypeValue) {
  77. ((SqlTypeValue) inValue).setTypeValue(ps, paramIndex, sqlType, typeName);
  78. }
  79. else if (inValue instanceof SqlValue) {
  80. ((SqlValue) inValue).setValue(ps, paramIndex);
  81. }
  82. else if (sqlType == Types.VARCHAR || sqlType == Types.NVARCHAR ||
  83. sqlType == Types.LONGVARCHAR || sqlType == Types.LONGNVARCHAR) {
  84. ps.setString(paramIndex, inValue.toString());
  85. }
  86. else if ((sqlType == Types.CLOB || sqlType == Types.NCLOB) && isStringValue(inValue.getClass())) {
  87. String strVal = inValue.toString();
  88. if (strVal.length() > 4000) {
  89. // Necessary for older Oracle drivers, in particular when running against an Oracle 10 database.
  90. // Should also work fine against other drivers/databases since it uses standard JDBC 4.0 API.
  91. if (sqlType == Types.NCLOB) {
  92. ps.setNClob(paramIndex, new StringReader(strVal), strVal.length());
  93. }
  94. else {
  95. ps.setClob(paramIndex, new StringReader(strVal), strVal.length());
  96. }
  97. return;
  98. }
  99. // Fallback: regular setString binding
  100. ps.setString(paramIndex, strVal);
  101. }
  102. else if (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC) {
  103. if (inValue instanceof BigDecimal) {
  104. ps.setBigDecimal(paramIndex, (BigDecimal) inValue);
  105. }
  106. else if (scale != null) {
  107. ps.setObject(paramIndex, inValue, sqlType, scale);
  108. }
  109. else {
  110. ps.setObject(paramIndex, inValue, sqlType);
  111. }
  112. }
  113. else if (sqlType == Types.BOOLEAN) {
  114. if (inValue instanceof Boolean) {
  115. ps.setBoolean(paramIndex, (Boolean) inValue);
  116. }
  117. else {
  118. ps.setObject(paramIndex, inValue, Types.BOOLEAN);
  119. }
  120. }
  121. else if (sqlType == Types.DATE) {
  122. if (inValue instanceof java.util.Date) {
  123. if (inValue instanceof java.sql.Date) {
  124. ps.setDate(paramIndex, (java.sql.Date) inValue);
  125. }
  126. else {
  127. ps.setDate(paramIndex, new java.sql.Date(((java.util.Date) inValue).getTime()));
  128. }
  129. }
  130. else if (inValue instanceof Calendar) {
  131. Calendar cal = (Calendar) inValue;
  132. ps.setDate(paramIndex, new java.sql.Date(cal.getTime().getTime()), cal);
  133. }
  134. else {
  135. ps.setObject(paramIndex, inValue, Types.DATE);
  136. }
  137. }
  138. else if (sqlType == Types.TIME) {
  139. if (inValue instanceof java.util.Date) {
  140. if (inValue instanceof java.sql.Time) {
  141. ps.setTime(paramIndex, (java.sql.Time) inValue);
  142. }
  143. else {
  144. ps.setTime(paramIndex, new java.sql.Time(((java.util.Date) inValue).getTime()));
  145. }
  146. }
  147. else if (inValue instanceof Calendar) {
  148. Calendar cal = (Calendar) inValue;
  149. ps.setTime(paramIndex, new java.sql.Time(cal.getTime().getTime()), cal);
  150. }
  151. else {
  152. ps.setObject(paramIndex, inValue, Types.TIME);
  153. }
  154. }
  155. else if (sqlType == Types.TIMESTAMP) {
  156. if (inValue instanceof java.util.Date) {
  157. if (inValue instanceof java.sql.Timestamp) {
  158. ps.setTimestamp(paramIndex, (java.sql.Timestamp) inValue);
  159. }
  160. else {
  161. ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
  162. }
  163. }
  164. else if (inValue instanceof Calendar) {
  165. Calendar cal = (Calendar) inValue;
  166. ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
  167. }
  168. else {
  169. ps.setObject(paramIndex, inValue, Types.TIMESTAMP);
  170. }
  171. }
  172. else if (sqlType == SqlTypeValue.TYPE_UNKNOWN || (sqlType == Types.OTHER &&
  173. "Oracle".equals(ps.getConnection().getMetaData().getDatabaseProductName()))) {
  174. if (isStringValue(inValue.getClass())) {
  175. ps.setString(paramIndex, inValue.toString());
  176. }
  177. else if (isDateValue(inValue.getClass())) {
  178. ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
  179. }
  180. else if (inValue instanceof Calendar) {
  181. Calendar cal = (Calendar) inValue;
  182. ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
  183. }
  184. else {
  185. // Fall back to generic setObject call without SQL type specified.
  186. ps.setObject(paramIndex, inValue);
  187. }
  188. }
  189. else {
  190. // Fall back to generic setObject call with SQL type specified.
  191. ps.setObject(paramIndex, inValue, sqlType);
  192. }
  193. }

至此update方法更新分析完毕

query

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

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

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

参数封装

接着同样是封装请求参数

  1. public <T> T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
  2. return query(sql, newArgPreparedStatementSetter(args), rse);
  3. }
  4. public <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
  5. return query(new SimplePreparedStatementCreator(sql), pss, rse);
  6. }

核心查询

  1. public <T> T query(
  2. PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
  3. throws DataAccessException {
  4. Assert.notNull(rse, "ResultSetExtractor must not be null");
  5. logger.debug("Executing prepared SQL query");
  6. //这里的execute的逻辑与update是一样的
  7. return execute(psc, new PreparedStatementCallback<T>() {
  8. @Override
  9. @Nullable
  10. public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
  11. ResultSet rs = null;
  12. try {
  13. if (pss != null) {
  14. // 同上
  15. pss.setValues(ps);
  16. }
  17. rs = ps.executeQuery();
  18. //这里是重点
  19. return rse.extractData(rs);
  20. }
  21. finally {
  22. JdbcUtils.closeResultSet(rs);
  23. if (pss instanceof ParameterDisposer) {
  24. ((ParameterDisposer) pss).cleanupParameters();
  25. }
  26. }
  27. }
  28. });
  29. }

处理返回值

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

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