Spring JDBC

环境搭建

  • 依赖

    1. compile(project(":spring-jdbc"))
    2. compile group: 'com.alibaba', name: 'druid', version: '1.1.21'
    3. compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.47'
  • db 配置

    1. jdbc.url=
    2. jdbc.driverClass=
    3. jdbc.username=
    4. jdbc.password=
  • 实体对象

    1. public class HsLog {
    2. private Integer id;
    3. private String source;
    4. public Integer getId() {
    5. return id;
    6. }
    7. public void setId(Integer id) {
    8. this.id = id;
    9. }
    10. public String getSource() {
    11. return source;
    12. }
    13. public void setSource(String source) {
    14. this.source = source;
    15. }
    16. }
  • DAO

    1. public interface HsLogDao {
    2. List<HsLog> findAll();
    3. void save(HsLog hsLog);
    4. }
  • 实现类

    ```java public class HsLogDaoImpl extends JdbcDaoSupport implements HsLogDao {

  1. @Override
  2. public List<HsLog> findAll() {
  3. return this.getJdbcTemplate().query("select * from hs_log", new HsLogRowMapper());
  4. }
  5. @Override
  6. public void save(HsLog hsLog) {
  7. this.getJdbcTemplate().update("insert into hs_log (SOURCE) values(?)"
  8. , new Object[]{
  9. hsLog.getSource(),
  10. }
  11. );
  12. }
  13. class HsLogRowMapper implements RowMapper<HsLog> {
  14. public HsLog mapRow(ResultSet rs, int rowNum) throws SQLException {
  15. HsLog log = new HsLog();
  16. log.setId(rs.getInt("id"));
  17. log.setSource(rs.getString("source"));
  18. return log;
  19. }
  20. }

}

  1. - xml
  2. ```xml
  3. <?xml version="1.0" encoding="UTF-8"?>
  4. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns="http://www.springframework.org/schema/beans"
  6. xmlns:context="http://www.springframework.org/schema/context"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans.xsd
  9. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"
  10. >
  11. <context:property-placeholder location="classpath:db.properties"/>
  12. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  13. <property name="url"
  14. value="${jdbc.url}"/>
  15. <property name="driverClassName" value="${jdbc.driverClass}"/>
  16. <property name="username" value="${jdbc.username}"/>
  17. <property name="password" value="${jdbc.password}"/>
  18. <!-- 配置监控统计拦截的filters -->
  19. <property name="filters" value="stat"/>
  20. <!-- 配置初始化大小、最小、最大 -->
  21. <property name="maxActive" value="20"/>
  22. <property name="initialSize" value="1"/>
  23. <property name="minIdle" value="1"/>
  24. <!-- 配置获取连接等待超时的时间 -->
  25. <property name="maxWait" value="60000"/>
  26. <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
  27. <property name="timeBetweenEvictionRunsMillis" value="60000"/>
  28. <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
  29. <property name="minEvictableIdleTimeMillis" value="300000"/>
  30. <property name="testWhileIdle" value="true"/>
  31. <property name="testOnBorrow" value="false"/>
  32. <property name="testOnReturn" value="false"/>
  33. <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
  34. <property name="poolPreparedStatements" value="true"/>
  35. <property name="maxOpenPreparedStatements" value="20"/>
  36. </bean>
  37. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  38. <property name="dataSource" ref="dataSource"></property>
  39. </bean>
  40. <bean id="hsLogDao" class="com.huifer.source.spring.dao.impl.HsLogDaoImpl">
  41. <property name="jdbcTemplate" ref="jdbcTemplate"/>
  42. </bean>
  43. </beans>
  • 运行方法

    1. public class SpringJDBCSourceCode {
    2. public static void main(String[] args) {
    3. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("JDBC-demo.xml");
    4. HsLogDaoImpl bean = applicationContext.getBean(HsLogDaoImpl.class);
    5. System.out.println(bean.findAll());
    6. HsLog hsLog = new HsLog();
    7. hsLog.setSource("jlkjll");
    8. bean.save(hsLog);
    9. }
    10. }

链接对象构造

Connection con = DataSourceUtils.getConnection(obtainDataSource());

  1. public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
  2. try {
  3. return doGetConnection(dataSource);
  4. }
  5. catch (SQLException ex) {
  6. throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
  7. }
  8. catch (IllegalStateException ex) {
  9. throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + ex.getMessage());
  10. }
  11. }

org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

  1. public static Connection doGetConnection(DataSource dataSource) throws SQLException {
  2. Assert.notNull(dataSource, "No DataSource specified");
  3. ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
  4. if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
  5. conHolder.requested();
  6. if (!conHolder.hasConnection()) {
  7. logger.debug("Fetching resumed JDBC Connection from DataSource");
  8. // 设置连接对象
  9. conHolder.setConnection(fetchConnection(dataSource));
  10. }
  11. return conHolder.getConnection();
  12. }
  13. // Else we either got no holder or an empty thread-bound holder here.
  14. logger.debug("Fetching JDBC Connection from DataSource");
  15. // 获取链接
  16. Connection con = fetchConnection(dataSource);
  17. // 当前线程支持同步
  18. if (TransactionSynchronizationManager.isSynchronizationActive()) {
  19. try {
  20. // Use same Connection for further JDBC actions within the transaction.
  21. // Thread-bound object will get removed by synchronization at transaction completion.
  22. // 在同一个事物中使用同一个链接对象
  23. ConnectionHolder holderToUse = conHolder;
  24. if (holderToUse == null) {
  25. holderToUse = new ConnectionHolder(con);
  26. }
  27. else {
  28. holderToUse.setConnection(con);
  29. }
  30. // 记录链接数量
  31. holderToUse.requested();
  32. TransactionSynchronizationManager.registerSynchronization(
  33. new ConnectionSynchronization(holderToUse, dataSource));
  34. holderToUse.setSynchronizedWithTransaction(true);
  35. if (holderToUse != conHolder) {
  36. TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
  37. }
  38. }
  39. catch (RuntimeException ex) {
  40. // Unexpected exception from external delegation call -> close Connection and rethrow.
  41. releaseConnection(con, dataSource);
  42. throw ex;
  43. }
  44. }
  45. return con;
  46. }

释放资源

releaseConnection(con, dataSource);

  • org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
  1. public static void releaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) {
  2. try {
  3. doReleaseConnection(con, dataSource);
  4. }
  5. catch (SQLException ex) {
  6. logger.debug("Could not close JDBC Connection", ex);
  7. }
  8. catch (Throwable ex) {
  9. logger.debug("Unexpected exception on closing JDBC Connection", ex);
  10. }
  11. }
  1. public static void doReleaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) throws SQLException {
  2. if (con == null) {
  3. return;
  4. }
  5. if (dataSource != null) {
  6. ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
  7. if (conHolder != null && connectionEquals(conHolder, con)) {
  8. // It's the transactional Connection: Don't close it.
  9. // 连接数-1
  10. conHolder.released();
  11. return;
  12. }
  13. }
  14. // 处理其他情况
  15. doCloseConnection(con, dataSource);
  16. }

org.springframework.transaction.support.ResourceHolderSupport

链接数

  1. /**
  2. * Increase the reference count by one because the holder has been requested
  3. * (i.e. someone requested the resource held by it).
  4. */
  5. public void requested() {
  6. this.referenceCount++;
  7. }
  8. /**
  9. * Decrease the reference count by one because the holder has been released
  10. * (i.e. someone released the resource held by it).
  11. */
  12. public void released() {
  13. this.referenceCount--;
  14. }

查询解析

org.springframework.jdbc.core.JdbcTemplate

  1. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  2. <property name="dataSource" ref="dataSource"></property>
  3. </bean>
  • 从配置中可以知道 JdbcTemplate 需要 dataSource 属性, 就从这里开始讲起
  • org.springframework.jdbc.support.JdbcAccessor.setDataSource, 这段代码就只做了赋值操作(依赖注入)
  1. public void setDataSource(@Nullable DataSource dataSource) {
  2. this.dataSource = dataSource;
  3. }
  • 下面hsLogDao也是依赖注入本篇不做详细讲述。

org.springframework.jdbc.core.JdbcTemplate#query(java.lang.String, org.springframework.jdbc.core.RowMapper)

  1. @Override
  2. public List<HsLog> findAll() {
  3. return this.getJdbcTemplate().query("select * from hs_log", new HsLogRowMapper());
  4. }
  1. @Override
  2. @Nullable
  3. public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
  4. Assert.notNull(sql, "SQL must not be null");
  5. Assert.notNull(rse, "ResultSetExtractor must not be null");
  6. if (logger.isDebugEnabled()) {
  7. logger.debug("Executing SQL query [" + sql + "]");
  8. }
  9. /**
  10. * Callback to execute the query.
  11. */
  12. class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
  13. @Override
  14. @Nullable
  15. public T doInStatement(Statement stmt) throws SQLException {
  16. ResultSet rs = null;
  17. try {
  18. // 执行sql
  19. rs = stmt.executeQuery(sql);
  20. // 1. org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData
  21. return rse.extractData(rs);
  22. }
  23. finally {
  24. JdbcUtils.closeResultSet(rs);
  25. }
  26. }
  27. @Override
  28. public String getSql() {
  29. return sql;
  30. }
  31. }
  32. return execute(new QueryStatementCallback());
  33. }
  1. @Override
  2. @Nullable
  3. public <T> T execute(StatementCallback<T> action) throws DataAccessException {
  4. Assert.notNull(action, "Callback object must not be null");
  5. Connection con = DataSourceUtils.getConnection(obtainDataSource());
  6. Statement stmt = null;
  7. try {
  8. stmt = con.createStatement();
  9. applyStatementSettings(stmt);
  10. // 执行
  11. T result = action.doInStatement(stmt);
  12. handleWarnings(stmt);
  13. return result;
  14. }
  15. catch (SQLException ex) {
  16. // Release Connection early, to avoid potential connection pool deadlock
  17. // in the case when the exception translator hasn't been initialized yet.
  18. String sql = getSql(action);
  19. JdbcUtils.closeStatement(stmt);
  20. stmt = null;
  21. DataSourceUtils.releaseConnection(con, getDataSource());
  22. con = null;
  23. throw translateException("StatementCallback", sql, ex);
  24. }
  25. finally {
  26. JdbcUtils.closeStatement(stmt);
  27. DataSourceUtils.releaseConnection(con, getDataSource());
  28. }
  29. }
  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. // 调用自定义的 rowMapper 进行数据处理
  7. T t = this.rowMapper.mapRow(rs, rowNum++);
  8. results.add(t);
  9. }
  10. return results;
  11. }

image-20200109150841916

这样就可以获取到了

方法result没有什么操作直接返回即可

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

插入解析

  1. @Override
  2. public void save(HsLog hsLog) {
  3. this.getJdbcTemplate().update("insert into hs_log (SOURCE) values(?)"
  4. , new Object[]{
  5. hsLog.getSource(),
  6. }
  7. );
  8. }

org.springframework.jdbc.core.JdbcTemplate#update(org.springframework.jdbc.core.PreparedStatementCreator, org.springframework.jdbc.core.PreparedStatementSetter)

  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. int rows = ps.executeUpdate();
  11. if (logger.isTraceEnabled()) {
  12. logger.trace("SQL update affected " + rows + " rows");
  13. }
  14. return rows;
  15. }
  16. finally {
  17. if (pss instanceof ParameterDisposer) {
  18. ((ParameterDisposer) pss).cleanupParameters();
  19. }
  20. }
  21. }));
  22. }