手写Spring:第19章-JDBC功能整合

文章目录

  • 一、目标:JDBC功能整合
  • 二、设计:JDBC功能整合
  • 三、实现:JDBC功能整合
    • 3.1 工程结构
    • 3.2 整合JDBC功能核心类图
    • 3.3 数据源操作
      • 3.3.1 数据源操作抽象类
      • 3.3.2 JDBC 工具类
    • 3.4 数据库执行
      • 3.4.1 语句处理器接口
      • 3.4.2 结果处理器接口
      • 3.4.3 行转列结果处理器实现类
      • 3.4.4 行映射器接口
      • 3.4.5 行列Map映射器实现类
      • 3.3.6 数据库执行接口
    • 3.5 数据库操作模板
      • 3.5.1 SQL提供者
      • 3.5.2 JDBC操作抽象类
      • 3.5.3 JDBC操作模板
  • 四、测试:JDBC功能整合
    • 4.1 添加测试配置
      • 4.1.1 sql数据库表
      • 4.1.2 Spring属性配置文件
      • 4.1.3 初始化 Spring和JdbcTemplate对象
    • 4.2 单元测试
      • 4.2.1 插入测试
      • 4.2.2 查询测试
  • 五、总结:JDBC功能整合

一、目标:JDBC功能整合

如何结合 Spring 框架,封装 JDBC 并对外提供统一的数据操作模板?

  • JDBC 的封装主要实现的是 JdbcTemplate 的功能。Spring 对数据库的操作在 JDBC 上做了深层次的封装。使用 Spring 的注入功能,可以将 DataSource 注册到 JdbcTemplate 中使用。
  • JdbcTemplate 主要提供的方法。
    • execute 方法:用于执行任何 SQL 语句,一般用于执行 DDL 语句。
    • update 方法及 batchUpdate 方法:
      • update 方法用于执行新增、修改、删除等语句。
      • batchUpdate 方法用于执行与批处理相关的语句。
    • query 方法及 queryForXXX 方法:用于执行与查询相关的语句。
    • call 方法:用于执行与存储过程、函数相关的语句。

二、设计:JDBC功能整合

设计:整合 JDBC 服务

  • Spring 框架将与 JDBC 的相关操作封装在 spring-jdbc 模块下的 JdbcTemplate 类中调用,并结合 Spring 提供的 InitializingBean 接口,在 BeanFactory 设置属性后进行相应的自定义初始化处理,将 JDBC 整合到 Spring 框架中。

手写Spring:第19章-JDBC功能整合_第1张图片

  • DB 连接池提供数据源服务,这里将 DruidDataSource 作为连接池使用。
  • 在引入连接池后,基于 JdbcTemplate 完成对数据库的操作处理,包括执行 SQL 语句(如查询、更新和删除数据库表等操作,以及开发对应的数据库表语句)。将执行后的结果进行封装,使用 ResultSetExtractor 接口、RowMapper 接口转换数据类型。

三、实现:JDBC功能整合

3.1 工程结构

spring-step-18
|-src
	|-main
	|	|-java
	|		|-com.lino.springframework
	|			|-aop
	|			|	|-aspectj
	|			|	|	|-AspectJExpressionPointcut.java
	|			|	|	|-AspectJExpressionPointcutAdvisor.java
	|			|	|-framework
	|			|	|	|-adapter
	|			|	|	|	|-MethodBeforeAdviceInterceptor.java
	|			|	|	|-autoproxy
	|			|	|	|	|-DefaultAdvisorAutoProxyCreator.java
	|			|	|	|-AopProxy.java
	|			|	|	|-Cglib2AopProxy.java
	|			|	|	|-JdkDynamicAopProxy.java
	|			|	|	|-ProxyFactory.java
	|			|	|	|-ReflectiveMethodInvocation.java
	|			|	|-AdvisedSupport.java
	|			|	|-Advisor.java
	|			|	|-BeforeAdvice.java
	|			|	|-ClassFilter.java
	|			|	|-MethodBeforeAdvice.java
	|			|	|-MethodMatcher.java
	|			|	|-Pointcut.java
	|			|	|-PointcutAdvisor.java
	|			|	|-TargetSource.java
	|			|-beans
	|			|	|-factory
	|			|	|	|-annotation
	|			|	|	|	|-Autowired.java
	|			|	|	|	|-AutowiredAnnotationBeanPostProcessor.java
	|			|	|	|	|-Qualifier.java
	|			|	|	|	|-Value.java
	|			|	|	|-config
	|			|	|	|	|-AutowireCapableBeanFactory.java
	|			|	|	|	|-BeanDefinition.java
	|			|	|	|	|-BeanFactoryPostProcessor.java
	|			|	|	|	|-BeanPostProcessor.java
	|			|	|	|	|-BeanReference.java
	|			|	|	|	|-ConfigurableBeanFactory.java
	|			|	|	|	|-InstantiationAwareBeanPostProcessor.java
	|			|	|	|	|-SingletonBeanRegistry.java
	|			|	|	|-support
	|			|	|	|	|-AbstractAutowireCapableBeanFactory.java
	|			|	|	|	|-AbstractBeabDefinitionReader.java
	|			|	|	|	|-AbstractBeabFactory.java
	|			|	|	|	|-BeabDefinitionReader.java
	|			|	|	|	|-BeanDefinitionRegistry.java
	|			|	|	|	|-CglibSubclassingInstantiationStrategy.java
	|			|	|	|	|-DefaultListableBeanFactory.java
	|			|	|	|	|-DefaultSingletonBeanRegistry.java
	|			|	|	|	|-DisposableBeanAdapter.java
	|			|	|	|	|-FactoryBeanRegistrySupport.java
	|			|	|	|	|-InstantiationStrategy.java
	|			|	|	|	|-SimpleInstantiationStrategy.java
	|			|	|	|-xml
	|			|	|	|	|-XmlBeanDefinitionReader.java
	|			|	|	|-Aware.java
	|			|	|	|-BeanClassLoaderAware.java
	|			|	|	|-BeanFactory.java
	|			|	|	|-BeanFactoryAware.java
	|			|	|	|-BeanNameAware.java
	|			|	|	|-ConfigurableListableBeanFactory.java
	|			|	|	|-DisposableBean.java
	|			|	|	|-FactoryBean.java
	|			|	|	|-HierarcgicalBeanFactory.java
	|			|	|	|-InitializingBean.java
	|			|	|	|-ListableBeanFactory.java
	|			|	|	|-ObjectFactory.java
	|			|	|	|-PropertyPlaceholderConfigurer.java
	|			|	|-BeansException.java
	|			|	|-PropertyValue.java
	|			|	|-PropertyValues.java
	|			|-context
	|			|	|-annotation
	|			|	|	|-ClassPathBeanDefinitionScanner.java
	|			|	|	|-ClassPathScanningCandidateComponentProvider.java
	|			|	|	|-Scope.java
	|			|	|-event
	|			|	|	|-AbstractApplicationEventMulticaster.java
	|			|	|	|-ApplicationContextEvent.java
	|			|	|	|-ApplicationEventMulticaster.java
	|			|	|	|-ContextclosedEvent.java
	|			|	|	|-ContextRefreshedEvent.java
	|			|	|	|-SimpleApplicationEventMulticaster.java
	|			|	|-support
	|			|	|	|-AbstractApplicationContext.java
	|			|	|	|-AbstractRefreshableApplicationContext.java
	|			|	|	|-AbstractXmlApplicationContext.java
	|			|	|	|-ApplicationContextAwareProcessor.java
	|			|	|	|-ClassPathXmlApplicationContext.java
	|			|	|	|-ConversionServiceFactoryBean.java
	|			|	|-ApplicationContext.java
	|			|	|-ApplicationContextAware.java
	|			|	|-ApplicationEvent.java
	|			|	|-ApplicationEventPublisher.java
	|			|	|-ApplicationListener.java
	|			|	|-ConfigurableApplicationContext.java
	|			|-core
	|			|	|-convert
	|			|	|	|-converter
	|			|	|	|	|-Converter.java
	|			|	|	|	|-ConverterFactory.java
	|			|	|	|	|-ConverterRegistry.java
	|			|	|	|	|-GenericConverter.java
	|			|	|	|-support
	|			|	|	|	|-DefaultConversionService.java
	|			|	|	|	|-GenericConversionService.java
	|			|	|	|	|-StringToNumberConverterFactory.java
	|			|	|	|-ConversionService
	|			|	|-io
	|			|	|	|-ClassPathResource.java
	|			|	|	|-DefaultResourceLoader.java
	|			|	|	|-FileSystemResource.java
	|			|	|	|-Resource.java
	|			|	|	|-ResourceLoader.java
	|			|	|	|-UrlResource.java
	|			|-jdbc
	|			|	|-core
	|			|	|	|-ColumnMapRowMapper.java
	|			|	|	|-JdbcOperations.java
	|			|	|	|-JdbcTemplate.java
	|			|	|	|-ResultSetExtractor.java
	|			|	|	|-RowMapper.java
	|			|	|	|-RowMapperResultSetExtractor.java
	|			|	|	|-SqlProvider.java
	|			|	|	|-StatementCallback.java
	|			|	|-datasource
	|			|	|	|-DataSourceUtils.java
	|			|	|-support
	|			|	|	|-JdbcAccessor.java
	|			|	|	|-JdbcUtils.java
	|			|-stereotype
	|			|	|-Component.java
	|			|-util
	|			|	|-ClassUtils.java
	|			|	|-NumberUtils.java
	|			|	|-StringValueResolver.java
	|-test
		|-java
			|-com.lino.springframework.test
                |-ApiTest.java
		|-resources
			|-spring.xml

3.2 整合JDBC功能核心类图

手写Spring:第19章-JDBC功能整合_第2张图片

  • DataSource 用于提供 Connection 链接操作,后续还会扩展辅助类(ConnectionHandlerConnectionHolder)与数据库事务结合。
  • JdbcTemplate 是执行数据库操作的入口类,提供 T execute(StatementCallback action, boolean closeResources) 对数据库操作的实现。
  • JdbcOperations 用于定义很多数据库操作,包括各类的查询处理。这些操作也会调用 execute 方法进行处理,再对数据进行封装。
    • 封装操作就是使用 ResultSetExtractor 接口、RowMapper 接口进行数据类型转换的。

3.3 数据源操作

3.3.1 数据源操作抽象类

DataSourceUtils.java

package com.lino.springframework.jdbc.datasource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @description: 数据源操作抽象类
 */
public abstract class DataSourceUtils {

    /**
     * 获取数据库连接
     *
     * @param dataSource 数据库对象
     * @return 数据库连接
     */
    public static Connection getConnection(DataSource dataSource) {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("Failed to obtain JDBC Connection", e);
        }
    }
}
  • DataSourceUtils 数据源的操作工具类提供了连接池的链接、关闭、释放等功能。这也是对 Spring 源码的简化。

3.3.2 JDBC 工具类

JdbcUtils.java

package com.lino.springframework.jdbc.support;

import cn.hutool.core.util.StrUtil;
import java.sql.*;

/**
 * @description: JDBC工具类
 */
public class JdbcUtils {

    /**
     * Determine the column name to use. The column name is determined based on a
     * lookup using ResultSetMetaData.
     * 

This method implementation takes into account recent clarifications * expressed in the JDBC 4.0 specification: *

columnLabel - the label for the column specified with the SQL AS clause. * If the SQL AS clause was not specified, then the label is the name of the column. * * @param resultSetMetaData the current meta-data to use * @param columnIndex the index of the column for the look up * @return the column name to use * @throws SQLException in case of lookup failure */ public static String lookupColumnName(ResultSetMetaData resultSetMetaData, int columnIndex) throws SQLException { String name = resultSetMetaData.getColumnLabel(columnIndex); if (StrUtil.isEmpty(name)) { name = resultSetMetaData.getColumnName(columnIndex); } return name; } /** * Retrieve a JDBC column value from a ResultSet, using the most appropriate * value type. The returned value should be a detached value object, not having * any ties to the active ResultSet: in particular, it should not be a Blob or * Clob object but rather a byte array or String representation, respectively. *

Uses the {@code getObject(index)} method, but includes additional "hacks" * to get around Oracle 10g returning a non-standard object for its TIMESTAMP * datatype and a {@code java.sql.Date} for DATE columns leaving out the * time portion: These columns will explicitly be extracted as standard * {@code java.sql.Timestamp} object. * * @param rs is the ResultSet holding the data * @param index is the column index * @return the value object * @throws SQLException if thrown by the JDBC API * @see java.sql.Blob * @see java.sql.Clob * @see java.sql.Timestamp */ public static Object getResultSetValue(ResultSet rs, int index) throws SQLException { Object obj = rs.getObject(index); String className = null; if (null != obj) { className = obj.getClass().getName(); } if (obj instanceof Blob) { Blob blob = (Blob) obj; obj = blob.getBytes(1, (int) blob.length()); } else if (obj instanceof Clob) { Clob clob = (Clob) obj; obj = clob.getSubString(1, (int) clob.length()); } else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) { obj = rs.getTimestamp(index); } else if (null != className && className.startsWith("oracle.sql.DATE")) { String metadataClassName = rs.getMetaData().getColumnClassName(index); if ("java.sql.Timestamp".equals(metadataClassName) || "oracle.sql.TIMESTAMP".equals(metadataClassName)) { obj = rs.getTimestamp(index); } else { obj = rs.getDate(index); } } else if (obj instanceof Date) { if ("java.sql.Timestamp".equals(rs.getMetaData().getColumnClassName(index))) { obj = rs.getDate(index); } } return obj; } }

3.4 数据库执行

3.4.1 语句处理器接口

StatementCallback.java

package com.lino.springframework.jdbc.core;

import java.sql.SQLException;
import java.sql.Statement;

/**
 * @description: 语句处理器
 */
public interface StatementCallback<T> {

    /**
     * 执行语句
     *
     * @param statement 语句对象
     * @return 泛型结果
     * @throws SQLException SQL异常
     */
    T doInStatement(Statement statement) throws SQLException;
}

3.4.2 结果处理器接口

ResultSetExtractor.java

package com.lino.springframework.jdbc.core;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @description: 结果返回器
 */
public interface ResultSetExtractor<T> {

    /**
     * 返回数据
     *
     * @param rs 结果集
     * @return 泛型结果
     * @throws SQLException SQL异常
     */
    T extractData(ResultSet rs) throws SQLException;
}

3.4.3 行转列结果处理器实现类

RowMapperResultSetExtractor.java

package com.lino.springframework.jdbc.core;

import cn.hutool.core.lang.Assert;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 行转列
 */
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;
    }

    @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()) {
            results.add(this.rowMapper.mapRow(rs, rowNum++));
        }
        return results;
    }
}

3.4.4 行映射器接口

RowMapper.java

package com.lino.springframework.jdbc.core;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @description: 行映射器接口
 */
public interface RowMapper<T> {

    /**
     * 返回行记录
     *
     * @param rs     结果集对象
     * @param rowNum 返回的行数
     * @return 泛型结果
     * @throws SQLException SQL异常
     */
    T mapRow(ResultSet rs, int rowNum) throws SQLException;
}

3.4.5 行列Map映射器实现类

ColumnMapRowMapper.java

package com.lino.springframework.jdbc.core;

import com.lino.springframework.jdbc.support.JdbcUtils;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @description: 行列Map映射器
 */
public class ColumnMapRowMapper implements RowMapper<Map<String, Object>> {

    @Override
    public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
        ResultSetMetaData rsMetaData = rs.getMetaData();
        int columnCount = rsMetaData.getColumnCount();
        Map<String, Object> mapOfColumnValues = createColumnMap(columnCount);
        for (int i = 1; i <= columnCount; i++) {
            String columnName = JdbcUtils.lookupColumnName(rsMetaData, i);
            mapOfColumnValues.putIfAbsent(getColumnKey(columnName), getColumnValue(rs, i));
        }
        return mapOfColumnValues;
    }

    protected Map<String, Object> createColumnMap(int columnCount) {
        return new LinkedHashMap<>(columnCount);
    }

    protected String getColumnKey(String columnName) {
        return columnName;
    }

    protected Object getColumnValue(ResultSet rs, int index) throws SQLException {
        return JdbcUtils.getResultSetValue(rs, index);
    }
}

3.3.6 数据库执行接口

JdbcOperations.java

package com.lino.springframework.jdbc.core;

import java.util.List;
import java.util.Map;

/**
 * @description: 数据库执行接口
 */
public interface JdbcOperations {

    /**
     * 执行语句处理器
     *
     * @param action 语句处理器
     * @param     泛型
     * @return 泛型结果
     * @throws Exception 异常
     */
    <T> T execute(StatementCallback<T> action) throws Exception;

    /**
     * 执行SQL语句
     *
     * @param sql SQL语句
     */
    void execute(String sql);

    /**
     * 执行查询
     *
     * @param sql SQL语句
     * @param res 结果返回器
     * @param  泛型
     * @return 泛型结果
     */
    <T> T query(String sql, ResultSetExtractor<T> res);

    /**
     * 执行查询
     *
     * @param sql       SQL语句
     * @param rowMapper 行对象
     * @param        泛型
     * @return 泛型集合结果
     */
    <T> List<T> query(String sql, RowMapper<T> rowMapper);

    /**
     * 查询列表
     *
     * @param sql SQL语句
     * @return Map集合
     */
    List<Map<String, Object>> queryForList(String sql);
}
  • Spring JDBC 框架中,JdbcOperations 的功能很简单,就是定义了一组用于 JDBC 操作的接口。

3.5 数据库操作模板

3.5.1 SQL提供者

SqlProvider.java

package com.lino.springframework.jdbc.core;

/**
 * @description: SQL提供者
 */
public interface SqlProvider {

    /**
     * 获取SQL语句
     *
     * @return SQL语句
     */
    String getSql();
}

3.5.2 JDBC操作抽象类

JdbcAccessor.java

package com.lino.springframework.jdbc.support;

import cn.hutool.core.lang.Assert;
import com.lino.springframework.beans.factory.InitializingBean;
import javax.sql.DataSource;

/**
 * @description: JDBC操作接口
 */
public abstract class JdbcAccessor implements InitializingBean {

    private DataSource dataSource;

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    protected DataSource obtainDataSource() {
        DataSource dataSource = getDataSource();
        Assert.state(dataSource != null, "No DataSource set");
        return dataSource;
    }

    @Override
    public void afterPropertiesSet() {
        if (getDataSource() == null) {
            throw new IllegalArgumentException("Property 'dataSource' is required");
        }
    }
}

3.5.3 JDBC操作模板

JdbcTemplate.java

package com.lino.springframework.jdbc.core;

import cn.hutool.core.lang.Assert;
import com.lino.springframework.jdbc.datasource.DataSourceUtils;
import com.lino.springframework.jdbc.support.JdbcAccessor;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

/**
 * @description: JDBC 操作模板
 */
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    /**
     * 查询大小
     */
    private int fetchSize = -1;
    /**
     * 最大行数
     */
    private int maxRows = -1;
    /**
     * 查询时间
     */
    private int queryTimeout = -1;

    public JdbcTemplate() {
    }

    public JdbcTemplate(DataSource dataSource) {
        setDataSource(dataSource);
        afterPropertiesSet();
    }

    public int getFetchSize() {
        return fetchSize;
    }

    public void setFetchSize(int fetchSize) {
        this.fetchSize = fetchSize;
    }

    public int getMaxRows() {
        return maxRows;
    }

    public void setMaxRows(int maxRows) {
        this.maxRows = maxRows;
    }

    public int getQueryTimeout() {
        return queryTimeout;
    }

    public void setQueryTimeout(int queryTimeout) {
        this.queryTimeout = queryTimeout;
    }

    @Override
    public <T> T execute(StatementCallback<T> action) {
        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        try {
            Statement stmt = con.createStatement();
            applyStatementSettings(stmt);
            return action.doInStatement(stmt);
        } catch (SQLException ex) {
            throw new RuntimeException("StatementCallback", ex);
        }
    }

    @Override
    public void execute(String sql) {
        class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {

            @Override
            public String getSql() {
                return sql;
            }

            @Override
            public Object doInStatement(Statement statement) throws SQLException {
                statement.execute(sql);
                return null;
            }
        }
        execute(new ExecuteStatementCallback());
    }

    @Override
    public <T> T query(String sql, ResultSetExtractor<T> res) {
        class QueryStatementCallback implements StatementCallback<T>, SqlProvider {

            @Override
            public String getSql() {
                return sql;
            }

            @Override
            public T doInStatement(Statement statement) throws SQLException {
                ResultSet rs = statement.executeQuery(sql);
                return res.extractData(rs);
            }
        }
        return execute(new QueryStatementCallback());
    }

    @Override
    public <T> List<T> query(String sql, RowMapper<T> rowMapper) {
        return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));
    }

    @Override
    public List<Map<String, Object>> queryForList(String sql) {
        return query(sql, new ColumnMapRowMapper());
    }

    private static <T> T result(T result) {
        Assert.state(null != result, "No result");
        return result;
    }

    protected void applyStatementSettings(Statement stat) throws SQLException {
        int fetchSize = getFetchSize();
        if (fetchSize != -1) {
            stat.setFetchSize(fetchSize);
        }

        int maxRows = getMaxRows();
        if (maxRows != -1) {
            stat.setMaxRows(maxRows);
        }
    }
}
  • JdbcTemplate 是对数据库操作的封装,在启动时由外部传入 DataSource 数据源,这也是处理数据库操作最基本的方法。
    • 通过这样的封装,减少了用户操作的复杂性,也符合设计模式的原则。
  • execute 方法是整个数据库操作的核心方法,将同类的数据操作进行统一封装。一些个性化的操作需要进行回调处理。
    • 例如:在 JdbcTemplate#query 方法中,也是对 JdbcTemplate#execute 进行包装操作,并返回处理结果。

四、测试:JDBC功能整合

4.1 添加测试配置

4.1.1 sql数据库表

user.sql

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `userId` varchar(9) DEFAULT NULL COMMENT '用户ID',
  `userHead` varchar(16) DEFAULT NULL COMMENT '用户头像',
  `createTime` datetime DEFAULT NULL COMMENT '创建时间',
  `updateTime` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

4.1.2 Spring属性配置文件

spring.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	         http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false&serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    bean>

    <bean id="jdbcTemplate" class="com.lino.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    bean>

beans>
  • spring.xml 配置文件中,先配置数据库的链接信息及库表,再将 dataSource 注入 JdbcTemplate 中,由 JdbcTemplate 完成数据库的操作。

4.1.3 初始化 Spring和JdbcTemplate对象

ApiTest.java

package com.lino.springframework.test;

import com.lino.springframework.context.support.ClassPathXmlApplicationContext;
import com.lino.springframework.jdbc.core.JdbcTemplate;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
import java.util.Map;

/**
 * @description: 测试类
 */
public class ApiTest {

    private JdbcTemplate jdbcTemplate;

    @Before
    public void init() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
    }
}

4.2 单元测试

4.2.1 插入测试

ApiTest.java

@Test
public void execute() {
    jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '123456789', '01-50', now(), now())");
}

测试结果

信息: {dataSource-1} inited

4.2.2 查询测试

ApiTest.java

@Test
public void queryForList() {
    List<Map<String, Object>> allResult = jdbcTemplate.queryForList("select * from user");
    for (Map<String, Object> objectMap : allResult) {
        System.out.println("测试结果:" + objectMap);
    }
}

测试结果

信息: {dataSource-1} inited
测试结果:{id=1, userId=123456789, userHead=01-50, createTime=2022-12-08 14:39:15.0, updateTime=2022-12-08 14:39:15.0}
  • 从测试结果看,这里已经把操作数据库的 JdbcTemplate 交由 Spring Bean 容器管理,并验证其数据库操作。

五、总结:JDBC功能整合

  • 本节主要介绍了 Spring Bean 容器的扩展功能,可以在指定的任何阶段把需要交给 Spring 管理的对象进行初始化。
    • 如:InitializingBean 可以在 BeanFactory 设置属性后进行相应的处理,整合其他对象。
  • 另外,需要熟悉 JDBC 的包装,使用支撑层 support 承接 Bean 对象的扩展。
    • DataSource 提供了操作数据源的功能,在 core 包中完成对数据库的操作并返回相应的结果。

你可能感兴趣的:(手写spring,spring,java)