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

news2024/11/18 15:47:29

文章目录

  • 一、目标: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 框架中。

在这里插入图片描述

  • 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功能核心类图

在这里插入图片描述

  • DataSource 用于提供 Connection 链接操作,后续还会扩展辅助类(ConnectionHandlerConnectionHolder)与数据库事务结合。
  • JdbcTemplate 是执行数据库操作的入口类,提供 T execute(StatementCallback<T> 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.
     * <p>This method implementation takes into account recent clarifications
     * expressed in the JDBC 4.0 specification:
     * <p><i>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</i>.
     *
     * @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.
     * <p>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 <T>    泛型
     * @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 <T> 泛型
     * @return 泛型结果
     */
    <T> T query(String sql, ResultSetExtractor<T> res);

    /**
     * 执行查询
     *
     * @param sql       SQL语句
     * @param rowMapper 行对象
     * @param <T>       泛型
     * @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

<?xml version="1.0" encoding="utf-8" ?>
<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&amp;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 包中完成对数据库的操作并返回相应的结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/984488.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式Linux驱动开发(LCD屏幕专题)(四)

单Buffer的缺点与改进方法 1. 单Buffer的缺点 如果APP速度很慢&#xff0c;可以看到它在LCD上缓慢绘制图案 即使APP速度很高&#xff0c;LCD控制器不断从Framebuffer中读取数据来显示&#xff0c;而APP不断把数据写入Framebuffer 假设APP想把LCD显示为整屏幕的蓝色、红色 很…

线程池的实现

目录 一、线程池的实现 1.什么是线程池 2.设计线程类 3.设计线程池类 4.运行 5.RAII加锁改造 二、利用单例模式改造线程池 1.复习 2.饿汉模式 3.懒汉模式 关于系统编程的知识我们已经学完了&#xff0c;最后我们需要利用之前写过的代码实现一个线程池&#xff0c;彻底…

如何理解张量、张量索引、切片、张量维度变换

Tensor 张量 Tensor&#xff0c;中文翻译“张量”&#xff0c;是一种特殊的数据结构&#xff0c;与数组和矩阵非常相似。在 PyTorch 中&#xff0c;使用张量对模型的输入和输出以及模型的参数进行编码。 Tensor 是一个 Python Class。PyTorch 官方文档中定义“Tensor&#xff0…

Datawhale × 和鲸科技丨《2023 中国人工智能人才学习白皮书》发布!

2023 是一个历史性的年份&#xff0c;它标志着人工智能技术的崛起与普及&#xff0c;这一年里&#xff0c;AI 不仅在科技、经济、社会、文化等各个领域取得突破性的进展&#xff0c;也在人类日常生活中扮演愈加重要的角色。随着人工智能时代的加速到来&#xff0c;我国 AI 人才…

msvcp140.dll丢失的有哪些解决方法,丢失msvcp140.dll是什么意思

在我们的日常生活中&#xff0c;电脑问题是无处不在的&#xff0c;而msvcp140.dll丢失又是其中比较常见的一种。msvcp140.dll是Microsoft Visual C运行时库的一部分&#xff0c;它包含了一些重要的函数和资源。当这个文件丢失时&#xff0c;可能会导致电脑出现各种问题&#xf…

链路追踪Skywalking快速入门

目录 1 Skywalking概述1.1 微服务系统监控三要素1.2 什么是链路追踪1.2.1 链路追踪1.2.2 OpenTracing1、数据模型&#xff1a;2、核心接口语义 1.3 常见APM系统1.4 Skywalking介绍1、SkyWalking 核心功能&#xff1a;2、SkyWalking 特点&#xff1a;3、Skywalking架构图&#x…

mysql之DML的select分组排序

目录 一、创建表employee和department表 1.创建department表 2.创建employee表 3.给employee表格和department表格建立外键 4.给department插入数据 5.给employee表插入数据 6.删除名字为那个的数据 二、分组查询和排序查询&#xff0c;以及对数据的处理&#xff08;av…

ARM/X86工业级数据采集 (DAQ) 与控制产品解决方案

I/O设备&#xff0c;包括信号调理模块、嵌入式PCI/PCIE卡、便携式USB模块、DAQ嵌入式计算机、模块化DAQ系统&#xff0c;以及DAQNavi/SDK软件开发包和DAQNavi/MCM设备状态监测软件。 工业I/O产品适用于各种工业自动化应用&#xff0c;从机器自动化控制、测试测量到设备状态监测…

Java“牵手”京东商品详情数据,京东商品详情API接口,京东API接口申请指南

京东平台商品详情接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取京东商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片等详细信息 。 获取商品详情接口API是一种用于获取电商平台上商品详情数据的接口&#xff0c;通过…

kuiper安装

1:使用docker方式安装 docker pull lfedge/ekuiper:latest docker run -p 9081:9081 -d --name kuiper -e MQTT_SOURCE__DEFAULT__SERVERtcp://127.0.0.1:1883 lfedge/ekuiper:latest这样就安装好了&#xff0c;但是操作只能通过命令完成&#xff0c;如果想要通过页面来操作&…

@DS注解方式springboot多数据源配置及失效场景解决

1.使用教程 导入依赖 <!--多数据源--><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version></dependency>配置数据源 datasource:…

MT36291 2.5A,高效型1.2MHz电流模式升压转换器芯片

MT36291 2.5A&#xff0c;高效型1.2MHz电流模式升压转换器芯片 特征 ●集成了80ms功率的MOSFET ●2.2V到16V的输入电压 ●1.2MHz固定开关频率 ●可调过电流保护&#xff1a; 0.5A ~2.5A ●内部2.5开关限流&#xff08;OC引脚浮动&#xff09; ●可调输出电压 ●内部补偿 ●过电…

PCL入门(三):矩阵变换实现平移和旋转

目录 1. pcl中的矩阵变换是什么2. 举例&#xff1a;如何做矩阵变换 1. pcl中的矩阵变换是什么 通过激光传感器等设备获得的3d点云在拼接成更大场景时&#xff0c;需要对点云数据进行旋转和平移操作。而旋转和平移操作&#xff0c;就可以通过矩阵变换来实现。 点的表示 对于点…

【每日一题】1523. 在区间范围内统计奇数数目,860. 柠檬水找零

1523. 在区间范围内统计奇数数目 - 力扣&#xff08;LeetCode&#xff09; 给你两个非负整数 low 和 high 。请你返回 low 和 high 之间&#xff08;包括二者&#xff09;奇数的数目。 示例 1&#xff1a; 输入&#xff1a;low 3, high 7 输出&#xff1a;3 解释&#xff1a;…

Excel文件损坏打不开怎么办?可用这三招解决!

当你的excel文件不可读&#xff0c;或者出现提示“文件已经被损坏&#xff0c;无法打开”&#xff0c;这种情况让人措手不及。而且还会给我们正常的工作带来很多麻烦&#xff0c;文件损坏打不开怎么办&#xff1f;来看看这3招&#xff0c;详细的图文教程&#xff0c;小白也能轻…

Notepad++ 的安装及配置

由于电脑重装了Win11系统&#xff0c;干脆重头开始&#xff0c;重新安装每一个软件~~~ 很多博客或者博主都会推荐notepad的官网&#xff1a;https://notepad-plus-plus.org/ 但大家亲自点开就会发现是无响应&#xff0c;如下图 同时&#xff0c;也会有很多博主直接给网盘地址…

【Flutter】Flutter 使用 table_calendar 实现自定义日历

【Flutter】Flutter 使用 table_calendar 实现自定义日历 文章目录 一、前言二、安装和基本使用三、日历的交互性四、日历事件五、自定义 UI 和 CalendarBuilders六、本地化和语言设置七、完整实际业务代码示例 一、前言 你好&#xff01;今天我要为你介绍一个非常实用的 Flut…

2023高教社杯数学建模国赛的工具箱准备

2023高教社杯数学建模国赛的工具箱准备 数学建模国赛工具箱&#xff08;私信领取&#xff09;&#xff01;&#xff01;&#xff01;小编仔细阅读了比赛官方网站上的规则和要求&#xff0c;以及比赛的题型和时间安排&#xff0c;现总结分享给大家。欢迎私信和评论&#xff0c;…

运维监控系统PIGOSS BSM 业务监控 大屏展现解析

“业务大屏”是 PIGOSS BSM&#xff08;IT运维监控工具&#xff09;的特色功能之一&#xff0c;旨在提供综合而直观的业务监控视图。该功能主要由三个组成部分构成&#xff1a;业务健康度雷达图、业务状态矩阵和多趋势对比图。 下面将对每个部分进行详细介绍&#xff1a; 业务健…