合肥网站建设公司加盟,代理网页在线,重庆建设工程信息网(管理平台),网站建设规定文章目录 第十八章 JDBC功能整合背景技术背景JDBC JdbcTemplate关键特性 用法示例业务背景 目标设计实现代码结构类图实现步骤 测试事先准备属性配置文件测试用例测试结果#xff1a; 总结 第十八章 JDBC功能整合
背景
技术背景
JDBC
JDBC#xff08;Java Database Conne… 文章目录 第十八章 JDBC功能整合背景技术背景JDBC JdbcTemplate关键特性 用法示例业务背景 目标设计实现代码结构类图实现步骤 测试事先准备属性配置文件测试用例测试结果 总结 第十八章 JDBC功能整合
背景
技术背景
JDBC
JDBCJava Database Connectivity是一个Java API用于连接和执行查询在数据库中。它提供了一种标准的方法允许Java程序连接到各种关系型数据库执行SQL语句并处理结果。JDBC是一个强大的工具它为Java应用程序提供了与数据库交互的能力无论是进行数据检索还是更新。通过使用JDBC开发者可以编写可移植的代码这些代码可以在不同的数据库系统之间运行而无需对代码进行大量修改。
以下是JDBC的一些关键点介绍
数据库连接 JDBC使用DriverManager类来管理数据库连接。通过传递数据库URL、用户名和密码可以获取到一个Connection对象该对象代表与特定数据库的连接。 驱动程序 JDBC驱动程序是一个允许Java应用程序与数据库进行交互的软件组件。JDBC驱动程序可以是类型1原生方法、类型2基于Java的独立驱动程序、类型3纯Java驱动程序使用JDBC网络协议、类型4JDBC驱动程序使用数据库的薄客户端。 Statement和PreparedStatement Statement对象用于执行静态SQL语句并返回它所生成的结果。PreparedStatement是Statement的子接口它允许预编译SQL语句可以提高性能并防止SQL注入攻击。 执行SQL语句 通过Statement或PreparedStatement对象可以执行各种SQL语句包括查询SELECT、更新UPDATE、DELETE、插入INSERT和DDLCREATE、DROP等语句。 处理结果 查询数据库后可以通过ResultSet对象处理返回的数据。ResultSet提供了一种方式来遍历查询结果集中的数据。 事务处理 JDBC支持事务处理默认情况下每个Statement执行的SQL语句都是自动提交的。可以通过Connection对象来设置自动提交模式并使用commit()和rollback()方法来管理事务。 批处理 JDBC提供了批处理功能允许一次执行多个SQL语句这可以减少网络往返次数提高性能。 元数据 DatabaseMetaData接口提供了关于数据库的整体元数据信息如表的结构、存储过程、支持的SQL语法等。 关闭资源 为了释放数据库资源应该在使用完毕后关闭ResultSet、Statement和Connection对象。 异常处理 JDBC操作可能会抛出SQLException因此需要适当的异常处理机制来确保程序的健壮性。
JdbcTemplate
JdbcTemplate 是 Spring 框架中提供的一个用于简化 JDBC 编程的模板类。它处理了资源的创建和释放并且提供了执行 SQL 语句和查询的便捷方法从而减少了编写 JDBC 代码时常见的样板代码。
以下是 JdbcTemplate 的一些关键特性和用法
关键特性 简化资源管理JdbcTemplate 管理数据库连接确保在每次执行后都正确关闭连接从而避免了资源泄漏。 异常处理它将 JDBC 的 SQLException 转换为 Spring 的 DataAccessException这使得异常处理更加一致和易于管理。 便捷的方法提供了多种便捷方法来执行 SQL 语句包括 update用于执行插入、更新、删除等、query用于执行查询以及 queryForObject、queryForMap、queryForList 等。 参数化查询支持使用 PreparedStatement 来执行参数化查询从而防止 SQL 注入攻击。 结果集处理提供了将结果集映射到 Java 对象的功能包括将单行结果映射到一个对象将多行结果映射到一个列表等。
用法示例
以下是一个简单的 JdbcTemplate 用法示例
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;public class JdbcTemplateExample {public static void main(String[] args) {// 配置数据源DriverManagerDataSource dataSource new DriverManagerDataSource();dataSource.setDriverClassName(com.mysql.cj.jdbc.Driver);dataSource.setUrl(jdbc:mysql://localhost:3306/yourdatabase);dataSource.setUsername(yourusername);dataSource.setPassword(yourpassword);// 创建 JdbcTemplate 实例JdbcTemplate jdbcTemplate new JdbcTemplate(dataSource);// 执行查询String sql SELECT * FROM your_table WHERE id ?;Object[] params new Object[] { 1 };MapString, Object result jdbcTemplate.queryForMap(sql, params);// 输出查询结果System.out.println(result);// 执行更新String updateSql UPDATE your_table SET name ? WHERE id ?;Object[] updateParams new Object[] { new name, 1 };int rowsAffected jdbcTemplate.update(updateSql, updateParams);// 输出更新结果System.out.println(Rows affected: rowsAffected);}
}在这个示例中我们首先配置了一个 DriverManagerDataSource然后创建了一个 JdbcTemplate 实例。接着我们使用 JdbcTemplate 执行了一个查询操作和一个更新操作并输出了相应的结果。
业务背景 日常开发过程中离不开和数据库的交互。JDBC提供了Java应用程序提供与数据库交互的能力无论是进行数据检索还是更新。 JDBC交互的套路操作有 配置数据库账号、密码、连接信息等参数 打开数据库连接 根据业务需求编写sql语句 预编译并执行sql语句 处理执行结果 处理抛出的异常 处理事务 关闭数据库连接 为了方便Spring框架与数据库的交互我们需要将上述过程进行抽象化实现jdbcTemplate模板类 来执行jdbc交互的大部分通用步骤形成spring-jdbc模块。spring-jdbc接收参数信息、JdbcTemplate接收业务需求sql进行处理并返回结果应用返回结果数据。
目标
基于当前实现的 Spring 框架实现jdbcTempate模板类完成spring对jdbc调用的封装。
设计
为了方便Spring框架与数据库的交互我们需要将JDBC交互的过程进行抽象化实现jdbcTemplate模板类 来执行jdbc交互的大部分通用步骤。整体设计结构如下图 首先定义好JdbcTemplate类需要支持的操作包括解析数据源参数、获取数据源、获取连接、预编译执行sql、处理执行结果、处理异常、关闭连接。 通过JdbcTemplate类封装了和JDBC的交互细节用户只需要通过调用jdbcTemplate方法就可以很方便的完成数据库交互。
实现
代码结构 源码实现https://github.com/swg209/spring-study/tree/main/step18-spring-jdbc
类图 在整个类图中通过JdbcAccessor实现InitializingBean将jdbcTemplate接入当前框架。数据源相关通过DataSourceUtils来接入JdbcTemplate实现了jdbcOperation定义的接口实现与数据库的交互操作。
DataSource
用来提供Connection之所以有那么多辅助类ConnectionHanlder、ConnectionHandle是为了可以和数据库事务结合
JdbcTemplate
这个类是进行执行操作的入口类里面有很多重载方法。 T execute(StatementCallback action, boolean closeResources) 这个是JdbcTemplate内部的私有实现方法JdbcOperations接口中定义的一系列execute()方法也是调用的该方法
JdbcOperations
这个接口定义了非常多的入口方法实现类就是JdbcTemplate T query(String sql, ResultSetExtractor rse) T query(String sql, ResultSetExtractor rse) 这个是query系列方法的最后调用方法该方法会调用JdbcTemplate内部的私有execute方法。
ResultSetExtractor
进行数据库查询之后会得到结果集即ResultSet这个类用于结果集数据提取
RowMapper
用于将结果集每行数据转换为需要的类型
实现步骤 数据源连接类DataSourceUtils提供Connection。 public abstract class DataSourceUtils {public static Connection getConnection(DataSource dataSource) {try {return doGetConnection(dataSource);} catch (SQLException e) {throw new CannotGetJdbcConnectionException(Failed to obtain JDBC Connection, e);}}public static Connection doGetConnection(DataSource dataSource) throws SQLException {Connection connection fetchConnection(dataSource);ConnectionHolder holderToUse new ConnectionHolder(connection);return connection;}private static Connection fetchConnection(DataSource dataSource) throws SQLException {Connection conn dataSource.getConnection();if (null conn) {throw new IllegalArgumentException(DataSource return null from getConnection(): dataSource);}return conn;}public static void releaseConnection(Connection con, DataSource dataSource) {try {doReleaseConnection(con, dataSource);} catch (SQLException ex) {
// logger.debug(Could not close JDBC Connection, ex);} catch (Throwable ex) {
// logger.debug(Unexpected exception on closing JDBC Connection, ex);}}public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {if (con null) {return;}doCloseConnection(con, dataSource);}public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {con.close();}
} 涉及连接的多个接口类包括ConnectionHandler、ConnectionHolder 以及实现类SimpleConnectionHandler。 public interface ConnectionHandler {Connection getConnection();default void releaseConnection(Connection con) {}}public class ConnectionHolder {private ConnectionHandler connectionHandler;private Connection currentConnection;public ConnectionHolder(ConnectionHandler connectionHandler) {this.connectionHandler connectionHandler;}public ConnectionHolder(Connection connection) {this.connectionHandler new SimpleConnectionHandler(connection);}public ConnectionHandler getConnectionHandler() {return connectionHandler;}protected boolean hasConnection() {return this.connectionHandler ! null;}protected void setConnection(Connection connection) {if (null ! this.currentConnection) {if (null ! this.connectionHandler) {this.connectionHandler.releaseConnection(this.currentConnection);}this.currentConnection null;}if (null ! connection) {this.connectionHandler new SimpleConnectionHandler(connection);} else {this.connectionHandler null;}}protected Connection getConnection() {Assert.notNull(this.connectionHandler, Active connection is required.);if (null this.currentConnection) {this.currentConnection this.connectionHandler.getConnection();}return this.currentConnection;}}public class SimpleConnectionHandler implements ConnectionHandler {private final Connection connection;public SimpleConnectionHandler(Connection connection) {Assert.notNull(connection, Connection must not be null);this.connection connection;}Overridepublic Connection getConnection() {return this.connection;}} 核心类 JdbcTemplate内部完成核心方法 public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {private T T execute(StatementCallbackT action, boolean closeResources) {Connection con DataSourceUtils.getConnection(obtainDataSource());Statement stmt null;try {stmt con.createStatement();applyStatementSettings(stmt);return action.doInStatement(stmt);} catch (SQLException e) {String sql getSql(action);JdbcUtils.closeStatement(stmt);stmt null;throw translateException(ConnectionCallback, sql, e);} finally {if (closeResources) {JdbcUtils.closeStatement(stmt);}}}private T T execute(PreparedStatementCreator psc, PreparedStatementCallbackT action, boolean closeResources) {Assert.notNull(psc, PreparedStatementCreator must not be null);Assert.notNull(action, Callback object must not be null);Connection con DataSourceUtils.getConnection(obtainDataSource());PreparedStatement ps null;try {ps psc.createPreparedStatement(con);applyStatementSettings(ps);T result action.doInPreparedStatement(ps);return result;} catch (SQLException ex) {String sql getSql(psc);psc null;JdbcUtils.closeStatement(ps);ps null;DataSourceUtils.releaseConnection(con, getDataSource());con null;throw translateException(PreparedStatementCallback, sql, ex);} finally {if (closeResources) {JdbcUtils.closeStatement(ps);DataSourceUtils.releaseConnection(con, getDataSource());}}}public T T query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractorT rse) {Assert.notNull(rse, ResultSetExtractor must not be null);return execute(psc, new PreparedStatementCallbackT() {Overridepublic 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);}}}, true);}......
}JdbcOperations 数据库交互操作接口. StatementCallback 会话回调接口用于执行任意的JDBC Statement操作RowMapper 行处理接口用于将结果集的每一行映射到Java对象ResultSetExtractor 结果集处理接口用于从整个ResultSet中提取数据。PreparedStatementSetter 预编译接口用于设置PreparedStatement的参数值。RowMapperResultSetExtractor 自定义接口或类用于结合RowMapper和ResultSetExtractor的功能来处理结果集。RawMapper - ColumnRawMapper 、 SingleColumnRawMapper. RawMapper原始映射器接口或类的泛称用于处理结果集的原始列数据。ColumnRawMapper列原始映射器用于将结果集的特定列映射到某个类型。SingleColumnRawMapper单列原始映射器用于将只返回一列数据的查询结果映射到某个类型。 public interface JdbcOperations {T T execute(StatementCallbackT action);void execute(String sql);//---------------------------------------------------------------------// query//---------------------------------------------------------------------T T query(String sql, ResultSetExtractorT res);T T query(String sql, Object[] args, ResultSetExtractorT rse);T ListT query(String sql, RowMapperT rowMapper);T ListT query(String sql, Object[] args, RowMapperT rowMapper);T T query(String sql, PreparedStatementSetter pss, ResultSetExtractorT rse);//---------------------------------------------------------------------// queryForList//---------------------------------------------------------------------ListMapString, Object queryForList(String sql);/*** 查询数据库表中某一个字段*/T ListT queryForList(String sql, ClassT elementType);T ListT queryForList(String sql, ClassT elementType, Object... args);ListMapString, Object queryForList(String sql, Object... args);//---------------------------------------------------------------------// queryForObject//---------------------------------------------------------------------T T queryForObject(String sql, RowMapperT rowMapper);T T queryForObject(String sql, Object[] args, RowMapperT rowMapper);/*** 查询数据库表中 某一条记录的 某一个字段*/T T queryForObject(String sql, ClassT requiredType);//---------------------------------------------------------------------// queryForMap//---------------------------------------------------------------------MapString, Object queryForMap(String sql);MapString, Object queryForMap(String sql, Object... args);}测试
事先准备
mysql数据库配置好连接信息, 建表语句。也可以后续执行ApiTest#executeSqlTest 完成建表
#创建数据库
CREATE DATABASE mybatis;#创建用户表
USE mybatis;CREATE TABLE user (id bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 主键id,username VARCHAR(100) NOT NULL DEFAULT COMMENT 用户名,PRIMARY KEY (id)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;
属性配置文件
spring.xml 配置数据库源连接信息注册jdbcTemplate bean。 我本地的mysql版本是8.0.33对应的mysql-connector-java也是 8.0.33留意pom.xml文件加上该依赖 amp; 是为了转义符号不加amp;allowPublicKeyRetrievaltrue会报java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
错误这个异常通常出现在尝试使用JDBC连接到MySQL数据库时特别是当使用SSL连接到MySQL 8.0或更高版本时。这个异常的原因是JDBC驱动程序默认不允许从服务器检索公钥这是出于安全考虑以防止中间人攻击MITM。?xml version1.0 encodingUTF-8?
beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlnshttp://www.springframework.org/schema/beansxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdbean iddataSourceclasscom.alibaba.druid.pool.DruidDataSourceproperty namedriverClass valuecom.mysql.cj.jdbc.Driver/property namejdbcUrlvaluejdbc:mysql://localhost:3306/mybatis?useSSLfalseamp;allowPublicKeyRetrievaltrue/property nameusername valueroot/property namepassword value123456//beanbean idjdbcTemplateclasscn.suwg.springframework.jdbc.support.JdbcTemplateproperty namedataSource refdataSource//bean/beans测试用例
public class ApiTest {private JdbcTemplate jdbcTemplate;Beforepublic void init() {ClassPathXmlApplicationContext applicationContext new ClassPathXmlApplicationContext(classpath:spring.xml);jdbcTemplate applicationContext.getBean(JdbcTemplate.class);}Testpublic void executeSqlTest() {jdbcTemplate.execute(CREATE TABLE user (\n id bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 主键id,\n username VARCHAR(100) NOT NULL DEFAULT COMMENT 用户名,\n PRIMARY KEY (id)\n ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;);}/*** 插入数据.*/Testpublic void executeInsertSqlTest() {//插入语句jdbcTemplate.execute(INSERT INTO user (username) values (小苏););}Testpublic void queryForListTest() {ListMapString, Object allResult jdbcTemplate.queryForList(select * from user);for (int i 0; i allResult.size(); i) {System.out.printf(第%d行数据, i 1);MapString, Object objectMap allResult.get(i);System.out.println(objectMap);}}Testpublic void queryListWithColumnClassTypeTest() {ListString allResult jdbcTemplate.queryForList(select username from user, String.class);for (int i 0; i allResult.size(); i) {System.out.printf(第%d行数据, i 1);String username allResult.get(i);System.out.println(username);}}Testpublic void queryListWithColumnClassTypeWithArgTest() {ListString allResult jdbcTemplate.queryForList(select username from user where id?, String.class, 1);for (int i 0; i allResult.size(); i) {System.out.printf(第%d行数据, i 1);String username allResult.get(i);System.out.println(username);}}Testpublic void queryListWithArgTest() {ListMapString, Object allResult jdbcTemplate.queryForList(select * from user where id?, 1);for (int i 0; i allResult.size(); i) {System.out.printf(第%d行数据, i 1);MapString, Object row allResult.get(i);System.out.println(row);}}Testpublic void queryObjectTest() {String username jdbcTemplate.queryForObject(select username from user where id1, String.class);System.out.println(username);}Testpublic void queryMapTest() {MapString, Object row jdbcTemplate.queryForMap(select * from user where id1);System.out.println(row);}Testpublic void queryMapWithArgTest() {MapString, Object row jdbcTemplate.queryForMap(select * from user where id?, 1);System.out.println(row);}
}
测试结果 queryForListTest queryListWithColumnClassTypeTest queryListWithColumnClassTypeWithArgTest queryListWithArgTest queryObjectTest queryMapTest queryMapWithArgTest
从测试结果中可以看到可以正常与数据库进行交互创建表、插入数据查询表数据、查询表中某列的数据、根据条件查询表的数据等功能。
总结
在本章节中我们深入探讨了JDBCJava Database Connectivity的功能整合特别是如何通过Spring框架中的JdbcTemplate来简化JDBC编程。我们详细分析了JDBC的核心组件、关键特性和用法并展示了如何通过JdbcTemplate来执行数据库操作包括查询、更新处理等。通过本节的学习我们可以借鉴这些代码的结构和风格提高自己的编码水平。可以帮助我们后续再遇到查询数据库遇到的问题时可以更快排查定位问题。 参考书籍《手写Spring渐进式源码实践》 书籍源代码https://github.com/fuzhengwei/small-spring