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的对象:Statement
和PreparedStatement
。
Statement
Statement
接口用于执行静态SQL语句,但存在SQL注入问题(字符串拼接)。
Statement stmt = conn.createStatement();
注意:
Statement
接口虽然简单易用,但在处理用户输入时容易受到SQL注入攻击。例如,如果用户输入的用户名或密码包含恶意SQL代码,可能会导致数据库泄露或被篡改。
PreparedStatement
PreparedStatement
是Statement
的子接口,支持预编译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()
方法向下移动游标。获取值的方法有多种,如getInt
、getString
等,可以根据字段类型选择合适的方法。
7. 关闭资源(先开的后关 后开的先关)
最后,必须关闭所有打开的资源,包括ResultSet
、Statement
和Connection
对象。关闭资源的代码一般放在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();}}
}
特点分析
-
优点:
-
简单直接,适合小型项目或快速原型开发
-
封装了基本的连接获取和资源关闭操作
-
-
缺点:
-
硬编码数据库连接信息,不利于维护
-
每次调用都会重复注册驱动(虽然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
改进点
-
配置文件支持:
-
将数据库连接信息移到
db.properties
文件中 -
通过
Properties
类加载配置
-
-
异常处理改进:
-
将检查异常转换为运行时异常,强制调用方处理
-
-
驱动加载优化:
-
使用
Class.forName()
替代直接注册驱动
-
仍然存在的问题
-
仍然没有连接池支持
-
资源管理可以更完善
-
配置文件路径硬编码
版本三:连接池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
核心优势
-
连接池支持:
-
使用Druid连接池,显著提高性能
-
连接可复用,减少创建和销毁开销
-
-
配置灵活性:
-
所有数据库配置(包括连接池参数)都通过
druid.properties
管理 -
可以轻松调整连接池大小、超时时间等参数
-
-
更健壮的异常处理:
-
构造阶段失败会抛出运行时异常
-
方法内部捕获异常但不吞掉,便于排查问题
-
-
资源管理优化:
-
虽然关闭逻辑与版本二类似,但配合连接池使用更合理
-
最佳实践建议
-
异常处理:
-
生产环境中应使用日志框架记录异常,而不是简单打印
-
考虑自定义异常类提供更友好的错误信息
-
-
资源管理:
-
使用try-with-resources语法(Java 7+)自动关闭资源
-
对于连接池,
close()
方法实际上是将连接返回到池中
-
-
连接池选择:
-
Druid是不错的选择,也可以考虑HikariCP(性能更好)
-
根据项目需求配置合适的连接池参数
-
-
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工具类的演进反映了软件开发中"封装重复、提高灵活性、优化性能"的普遍原则:
-
基础版:解决基本功能,适合简单场景
-
配置化版:通过外部配置提高灵活性
-
连接池版:引入连接池提升性能和资源利用率
在实际项目中,建议直接使用成熟的ORM框架或经过充分测试的JDBC工具类库,除非有特殊需求需要自定义实现。