当前位置: 首页 > news >正文

JDBC工具类

目录

引言

一、JDBC连接数据库步骤

1. 加载驱动

2. 获取连接(URL 用户名  密码)

3. 编写sql

4. 获取执行sql的stmt的对象

5. 执行sql  拿到结果集

6. 遍历结果集

7. 关闭资源(先开的后关 后开的先关)

二、JDBC工具类

版本一:基础JDBC工具类(JdbcUtils)

版本二:配置化JDBC工具类(JdbcUtils2)

版本三:连接池JDBC工具类(JdbcUtils3)

测试

总结


引言

        JDBC(Java Database Connectivity)是Java连接数据库的标准API,但在实际开发中直接使用原始JDBC API会带来大量重复代码。本文将介绍三种不同版本的JDBC工具类实现,展示如何通过逐步优化来提高开发效率和代码质量。

一、JDBC连接数据库步骤

1. 加载驱动

        首先,需要加载数据库驱动。驱动是各个数据库生产商提供的实现类,用于实现JDBC接口规范。对于MySQL数据库,需要导入mysql-connector-java-5.1.13-bin.jar驱动包。

        在Java代码中,通常使用以下方式加载驱动:

Class.forName("com.mysql.jdbc.Driver");

​注意​​:通过Class.forName("com.mysql.jdbc.Driver")加载驱动,可以避免直接使用new Driver()方式带来的依赖问题和驱动jar包加载两次的问题。

2. 获取连接(URL 用户名  密码)

        加载驱动后,需要获取数据库连接。使用DriverManager类的getConnection方法,传入数据库连接URL、用户名和密码。

String url = "jdbc:mysql:///spring_db"; // 如果是本地数据库,localhost:3306可以省略
String user = "root";
String password = "12345";
Connection conn = DriverManager.getConnection(url, username, password);

​URL格式​​:jdbc:mysql://localhost:3306/spring_db

  • jdbc:主协议

  • mysql:子协议(可能变化)

  • localhost:主机

  • 3306:默认端口号

  • spring_db:数据库名称

3. 编写sql

        接下来,编写需要执行的SQL语句。SQL语句可以是查询、插入、更新或删除操作。

String sql = "SELECT * FROM t_user";

4. 获取执行sql的stmt的对象

        有两种方式获取执行SQL的对象:StatementPreparedStatement

Statement

   Statement接口用于执行静态SQL语句,但存在SQL注入问题(字符串拼接)。

Statement stmt = conn.createStatement();

​注意​​:Statement接口虽然简单易用,但在处理用户输入时容易受到SQL注入攻击。例如,如果用户输入的用户名或密码包含恶意SQL代码,可能会导致数据库泄露或被篡改。

PreparedStatement

   PreparedStatementStatement的子接口,支持预编译SQL语句,采用占位符的形式,能有效防止SQL注入问题。推荐使用。

String sql = "SELECT * FROM t_user WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username); // 设置第一个参数
pstmt.setString(2, password); // 设置第二个参数

​注意​​:使用PreparedStatement时,SQL语句中的参数用?占位符代替,再通过setXxx方法设置具体值。这种方式不仅提高了代码的可读性和可维护性,还能有效防止SQL注入攻击。

5. 执行sql  拿到结果集

        执行SQL语句并获取结果集。如果是查询语句,返回的结果集封装在ResultSet接口中。

ResultSet rs = pstmt.executeQuery(); // 查询语句
// int affectedRows = pstmt.executeUpdate(); // 增删改语句

​注意​​:executeQuery方法用于执行查询语句,返回一个ResultSet对象;executeUpdate方法用于执行增删改语句,返回受影响的行数。

6. 遍历结果集

        遍历ResultSet结果集,获取查询的数据。

while (rs.next()) {int id = rs.getInt("id");String username = rs.getString("username");String password = rs.getString("password");String email = rs.getString("email");System.out.println(id + ", " + username + ", " + password + ", " + email);
}

​注意​​:ResultSet内部维护一个游标,默认指向第一行数据之前,调用next()方法向下移动游标。获取值的方法有多种,如getIntgetString等,可以根据字段类型选择合适的方法。

7. 关闭资源(先开的后关 后开的先关)

        最后,必须关闭所有打开的资源,包括ResultSetStatementConnection对象。关闭资源的代码一般放在finally块中,确保一定会执行。

if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}
}
if (pstmt != null) {try {pstmt.close();} catch (SQLException e) {e.printStackTrace();}
}
if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}
}

​注意​​:关闭资源的顺序应与打开的顺序相反,即先开的后关,后开的先关。这样可以确保资源的正确释放,避免内存泄漏和资源占用问题。

        通过以上步骤,我们可以使用JDBC连接数据库并执行SQL操作。结合PreparedStatement可以有效防止SQL注入问题,确保数据库操作的安全性。

二、JDBC工具类

版本一:基础JDBC工具类(JdbcUtils)

        这是最基础的JDBC工具类实现(加载驱动,获取连接和关闭资源可提取出来,重复使用,即JDBC工具类1.0版本),主要解决了驱动加载和连接获取的基本需求。

package com.qcby.utils;import com.mysql.jdbc.Driver;import java.sql.*;/*** JDBC工具类 简化开发* 减少重复性代码* JDBC的工具类 1.0版本*/
public class JdbcUtils {/*** 加载驱动*/public static void createDriver(){try{//2种//1.直接调用方法DriverManager.registerDriver(new Driver());//2.反射加载//Class.forName("com.mysql.jdbc.Driver");}catch (Exception e){e.printStackTrace();}}/*** 获取连接* @return*/public static Connection getConnection(){Connection connection=null;try {//1.加载驱动createDriver();//2.获取连接connection=DriverManager.getConnection("jdbc:mysql:///spring_db","root","12345");}catch (SQLException e){e.printStackTrace();}return connection;}/*** 关闭资源*///1.查询 重载方法public static void close(Connection connection, ResultSet resultSet, Statement statement){try{resultSet.close();statement.close();connection.close();}catch (SQLException e){e.printStackTrace();}}//2.增删改public static void close(Connection connection, Statement statement){try{statement.close();connection.close();}catch (SQLException e){e.printStackTrace();}}
}

特点分析

  1. ​优点​​:

    • 简单直接,适合小型项目或快速原型开发

    • 封装了基本的连接获取和资源关闭操作

  2. ​缺点​​:

    • 硬编码数据库连接信息,不利于维护

    • 每次调用都会重复注册驱动(虽然JDBC规范允许重复注册,但不推荐)

    • 缺乏异常处理机制

    • 没有连接池支持,性能较差

版本二:配置化JDBC工具类(JdbcUtils2)

        这个版本通过读取配置文件来提高灵活性,解决了硬编码问题。

package com.qcby.utils;import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;/*** JDBC的工具类 2.0版本(智能一些),编写properties属性文件,程序就可以读取属性文件*      1. 驱动类*      2. 数据库地址*      3. 用户名*      4. 密码*/
public class JdbcUtils2 {//静态不可更改常量private static final String diverclass;private static final String url;private static final String username;private static final String password;//静态代码块 类一加载的时候就会执行,调用的这个类的时候static {//加载db.properties文件读取链接数据库的所有参数// 加载属性文件Properties pro = new Properties();InputStream inputStream = JdbcUtils.class.getResourceAsStream("/db.properties");// 加载属性文件try {pro.load(inputStream);} catch (IOException e) {e.printStackTrace();}// 给常量赋值diverclass = pro.getProperty("driverclass");url = pro.getProperty("url");username = pro.getProperty("username");password = pro.getProperty("password");}/*** 加载驱动*/public static void createDriver(){try{//2种//1.直接调用方法//DriverManager.registerDriver(new Driver());//2.反射加载Class.forName(diverclass);}catch (Exception e){e.printStackTrace();}}/*** 获取连接* @return*/public static Connection getConnection(){Connection connection=null;try {//1.加载驱动createDriver();//2.获取连接connection= DriverManager.getConnection(url,username,password);}catch (SQLException e){e.printStackTrace();}return connection;}/*** 关闭资源*///1.查询public static void close(Connection connection, ResultSet resultSet, Statement statement){try{resultSet.close();statement.close();connection.close();}catch (SQLException e){e.printStackTrace();}}//2.增删改public static void close(Connection connection, Statement statement){try{statement.close();connection.close();}catch (SQLException e){e.printStackTrace();}}
}
#db.properties
driverclass=com.mysql.jdbc.Driver
url=jdbc:mysql:///spring_db
username=root
password=12345

改进点

  1. ​配置文件支持​​:

    • 将数据库连接信息移到db.properties文件中

    • 通过Properties类加载配置

  2. ​异常处理改进​​:

    • 将检查异常转换为运行时异常,强制调用方处理

  3. ​驱动加载优化​​:

    • 使用Class.forName()替代直接注册驱动

仍然存在的问题

  1. 仍然没有连接池支持

  2. 资源管理可以更完善

  3. 配置文件路径硬编码

版本三:连接池JDBC工具类(JdbcUtils3)

        这是最完善的版本,引入了连接池技术(Druid)来提高性能和资源利用率。

package com.qcby.utils;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;/*** JDBC的工具类 1.0版本* JDBC的工具类 2.0版本(智能一些),编写properties属性文件,程序就可以读取属性文件* JDBC的工具类 3.0版本,加入连接池对象*/
public class JdbcUtils3 {// 连接池对象private static DataSource DATA_SOURCE;static{// 加载属性文件Properties pro = new Properties();InputStream inputStream = JdbcUtils3.class.getResourceAsStream("/druid.properties");try {// 加载属性文件pro.load(inputStream);// 创建连接池对象DATA_SOURCE = DruidDataSourceFactory.createDataSource(pro);} catch (Exception e) {e.printStackTrace();}}/*** 从连接池中获取连接,返回。* @return*/public static Connection getConnection(){Connection conn = null;try {conn = DATA_SOURCE.getConnection();} catch (SQLException e) {e.printStackTrace();}return conn;}/*** 关闭资源* @param conn* @param stmt* @param rs*/public static void close(Connection conn, ResultSet rs, Statement stmt){if(rs != null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 关闭资源* @param conn* @param stmt*/public static void close(Connection conn, Statement stmt){if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}
}
#druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///spring_db
username=root
password=12345
initialSize=5
maxActive=10
maxWait=3000
maxIdle=6
minIdle=3

核心优势

  1. ​连接池支持​​:

    • 使用Druid连接池,显著提高性能

    • 连接可复用,减少创建和销毁开销

  2. ​配置灵活性​​:

    • 所有数据库配置(包括连接池参数)都通过druid.properties管理

    • 可以轻松调整连接池大小、超时时间等参数

  3. ​更健壮的异常处理​​:

    • 构造阶段失败会抛出运行时异常

    • 方法内部捕获异常但不吞掉,便于排查问题

  4. ​资源管理优化​​:

    • 虽然关闭逻辑与版本二类似,但配合连接池使用更合理

最佳实践建议

  1. ​异常处理​​:

    • 生产环境中应使用日志框架记录异常,而不是简单打印

    • 考虑自定义异常类提供更友好的错误信息

  2. ​资源管理​​:

    • 使用try-with-resources语法(Java 7+)自动关闭资源

    • 对于连接池,close()方法实际上是将连接返回到池中

  3. ​连接池选择​​:

    • Druid是不错的选择,也可以考虑HikariCP(性能更好)

    • 根据项目需求配置合适的连接池参数

  4. ​SQL操作​​:

    • 对于生产代码,应使用PreparedStatement防止SQL注入

    • 考虑使用ORM框架(MyBatis、Hibernate)进一步简化开发

特性1.0版本2.0版本3.0版本
配置管理硬编码配置文件连接池专属配置
连接创建每次新建每次新建连接池复用
性能表现差(100ms/次)优(<10ms/次)
并发支持队列机制
适用场景学习/测试小型项目生产环境

测试

package com.qcby.model;/*** 对应数据库的账户表* 实体类*/
public class Account {private Integer id;private String name;private Double money;public Account(){}public Account(Integer id, String name, Double money) {this.id = id;this.name = name;this.money = money;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}
package com.qcby.dao;import com.qcby.model.Account;
import com.qcby.utils.JdbcUtils;
import com.qcby.utils.JdbcUtils2;
import com.qcby.utils.JdbcUtils3;import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;public class JdbcDao {public void select01(){Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {//jdbc连接数据库//1.加载驱动//2.获取连接(URL 用户名  密码)connection = JdbcUtils.getConnection();//3.编写sqlString sql="select * from account";//4.获取执行sql的stmt的对象statement = connection.createStatement();// 两个  stmt(sql注入问题 字符串拼接)  pstmt(预编译 防止sql植入问题  占位符))//5.执行sql  拿到结果集resultSet = statement.executeQuery(sql);//6.遍历结果集while (resultSet.next()){Account account=new Account();account.setId(resultSet.getInt("id"));account.setName(resultSet.getString("username"));account.setMoney(resultSet.getDouble("money"));System.out.println(account);}} catch (SQLException e) {e.printStackTrace();}finally {//7.关闭资源(先开的后关 后开的先关)JdbcUtils.close(connection, resultSet, statement);}}public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {//jdbc连接数据库//1.加载驱动//2.获取连接(URL 用户名  密码)connection = JdbcUtils3.getConnection();//3.编写sqlString sql="select * from account";//4.获取执行sql的stmt的对象statement = connection.createStatement();// 两个  stmt(sql注入问题 字符串拼接)  pstmt(预编译 防止sql植入问题  占位符))//5.执行sql  拿到结果集resultSet = statement.executeQuery(sql);//6.遍历结果集while (resultSet.next()){Account account=new Account();account.setId(resultSet.getInt("id"));account.setName(resultSet.getString("name"));account.setMoney(resultSet.getDouble("money"));System.out.println(account);}} catch (SQLException e) {e.printStackTrace();}finally {//7.关闭资源(先开的后关 后开的先关)JdbcUtils3.close(connection, resultSet, statement);}}//    public void select(){
//        //jdbc连接数据库
//        //1.加载驱动
//        //2.获取连接(URL 用户名  密码)
//        //3.编写sql
//        //4.获取执行sql的stmt的对象
//        // 两个  stmt(sql注入问题 字符串拼接)  pstmt(预编译 防止sql植入问题  占位符))
//        //5.执行sql  拿到结果集
//        //6.遍历结果集
//        //7.关闭资源(先开的后关 后开的先关)
//    }
}

总结

JDBC工具类的演进反映了软件开发中"封装重复、提高灵活性、优化性能"的普遍原则:

  1. ​基础版​​:解决基本功能,适合简单场景

  2. ​配置化版​​:通过外部配置提高灵活性

  3. ​连接池版​​:引入连接池提升性能和资源利用率

在实际项目中,建议直接使用成熟的ORM框架或经过充分测试的JDBC工具类库,除非有特殊需求需要自定义实现。

相关文章:

  • 基于Spring Boot + Vue的母婴商城系统( 前后端分离)
  • PHP框架在分布式系统中的应用!
  • python04——条件判断(选择结构)
  • OLE(对象链接与嵌入)剪贴板内容插入到 CAD 图形中——CAD c# 二次开发
  • Kubernetes生产实战(十二):无工具容器网络连接数暴增指南
  • Baklib加速企业AI数据准备实践
  • 用AI写简历是否可行?
  • 逆波兰表达式求值(中等)
  • 第20章 Python数据类型详解:字典进阶
  • K8S Svc Port-forward 访问方式
  • 计算机网络八股文--day1
  • [ctfshow web入门] web70
  • 【计算机视觉】OpenCV实战项目:Athlete-Pose-Detection 运动员姿态检测系统:基于OpenCV的实时运动分析技术
  • 每天五分钟机器学习:拉格朗日对偶函数
  • 串口屏调试 1.0
  • 深入解析Vue3中ref与reactive的区别及源码实现
  • 居然智家亮相全零售AI火花大会 AI大模型赋能家居新零售的进阶之路
  • 银河麒麟桌面V10-SP1-2303操作系统V10加固手册
  • 【Linux】基础 IO(一)
  • 深入浅出之STL源码分析2_类模版
  • 鄂州:锁死中小学教师编制总量,核减小学编制五百名增至初中
  • 巴基斯坦全面恢复领空开放
  • 印度军方否认S-400防空系统被摧毁
  • 《尤物公园》连演8场:观众上台,每一场演出都独一无二
  • 道指跌逾100点,特斯拉涨近5%
  • 伤员回归新援融入,海港逆转海牛重回争冠集团