引言:JDBC在Spring生态中的重要性
在Java生态中,JDBC(Java Database Connectivity)是连接应用程序与数据库的核心技术。Spring框架通过其强大的JDBC支持,极大地简化了数据库操作,减少了样板代码,提高了开发效率。本文将带您探索JDBC技术的演进历程,并展示Spring如何优雅地解决传统JDBC的痛点。
一、JDBC技术的演进之路
1. 基础JDBC:原始连接方式
// JDBC.java - 最基础的JDBC操作
public class JDBC {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/bishe?serverTimezone=UTC",
"root",
"2020"
);
stmt = conn.createStatement();
String sql = "SELECT id,`name`,studentID FROM student WHERE id=1";
rs = stmt.executeQuery(sql);
if (rs.next()) {
Student student = new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setStudentID(rs.getString("studentID"));
System.out.println(student);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 繁琐的资源关闭...
try {
if (rs != null) rs.close();
} catch (Exception e) { e.printStackTrace(); }
try {
if (stmt != null) stmt.close();
} catch (Exception e) { e.printStackTrace(); }
try {
if (conn != null) conn.close();
} catch (Exception e) { e.printStackTrace(); }
}
}
}
痛点分析:
-
重复的样板代码(驱动加载、连接获取、资源关闭)
-
硬编码的数据库配置
-
异常处理冗长
-
资源管理容易出错
2. JDBC工具类进化史
版本1.0:基础封装
package com.qcby.utils;
import java.sql.*;
public class JDBC02 {
/**
* 加载驱动的方法 static
*/
public static void loadDrive(){
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 连接对象conn
*/
public static Connection getConntion(){
loadDrive();
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bishe", "root", "2020");
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
/**
* 获取执行sql的对象 Statement
*/
public static Statement creatstmt() throws SQLException {
Connection conntion = getConntion();
Statement statement = conntion.createStatement();
return statement;
}
/**
* 关闭资源的提取
*/
public static void close(ResultSet rs, Statement stmt, Connection conn){
try {
rs.close();
stmt.close();
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
测试用例
@Test
public void jdbc02() {//对原有的多次操作会重复的操作函数进行了封装使得编码变得简单
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 获取数据库连接
conn = JDBC02.getConntion();
// 2. 创建Statement对象
stmt = conn.createStatement();
// 3. 执行查询语句(示例:查询student表)
String sql = "SELECT id, name FROM student WHERE id =1";
rs = stmt.executeQuery(sql);
// 4. 处理结果集
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println("ID: " + id
+ ", Name: " + name);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 5. 关闭资源(使用工具类方法)
JDBC02.close(rs, stmt, conn);
}
}
结果如图所示
版本2.0:配置文件分离
package com.qcby.utils;
import com.alibaba.druid.util.JdbcUtils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JDBC03 {
private static final String driverclass;
private static final String url;
private static final String username;
private static final String password;
static{
// 加载属性文件
Properties pro = new Properties();
InputStream inputStream = JdbcUtils.class.getResourceAsStream("/db.properties");
try {
// 加载属性文件
pro.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
// 给常量赋值
driverclass = pro.getProperty("driverclass");
url = pro.getProperty("url");
username = pro.getProperty("username");
password = pro.getProperty("password");
}
/**
* 加载驱动
*/
public static void loadDriver(){
try {
// 加载驱动类
Class.forName(driverclass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 加载完驱动,获取到连接,返回连接对象
* @return
*/
public static Connection getConnection(){
// 加载驱动
loadDriver();
// 获取到连接对象,返回
Connection conn = null;
try {
// 获取到连接
conn = DriverManager.getConnection(url,username,password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭资源
* @param conn
* @param stmt
* @param rs
*/
public static void close(Connection conn, Statement stmt, ResultSet rs){
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();
}
}
}
}
//db.properties配置文件
driverclass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bishe?useSSL=false&serverTimezone=UTC
username=root
password=2020
测试用例
@Test
public void jdbc03() {//使用配置文件将一些写死的数据传进去使得更加灵活
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;//结果集
try {
// 1. 获取数据库连接
conn = JDBC03.getConnection();
// 2. 创建预编译语句(示例:查询用户表)
String sql = "SELECT id, name FROM student WHERE id = 1"; // 使用参数化查询
pstmt = conn.prepareStatement(sql);
// 4. 执行查询
rs = pstmt.executeQuery();
// 5. 处理结果集
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.printf("ID: %d, Name: %s%n", id, name);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6. 关闭资源
JDBC03.close(conn, pstmt, rs);
}
}
测试结果
版本3.0:连接池集成
package com.qcby.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* JDBC的工具类 1.0版本
* JDBC的工具类 2.0版本(智能一些),编写properties属性文件,程序就可以读取属性文件
* JDBC的工具类 3.0版本,加入连接池对象
*/
public class JDBC04 {
// 连接池对象
private static DataSource DATA_SOURCE;
static{
// 加载属性文件
Properties pro = new Properties();
InputStream inputStream = JDBC04.class.getResourceAsStream("/lianjiechi.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, Statement stmt, ResultSet rs){
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();
}
}
}
}
//lianjiechi.properties连接池配置文件
# 基本配置
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bishe?useSSL=false&serverTimezone=UTC
username=root
password=2020
# 连接池配置
initialSize=5
minIdle=5
maxActive=20
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=true
maxPoolPreparedStatementPerConnectionSize=20
filters=stat,wall,log4j
测试用例
@Test
public void jdbc04() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 1. 从Druid连接池获取连接
conn = JDBC04.getConnection();
// 2. 创建预编译SQL语句(示例:查询student表)
String sql = "SELECT id,`name` FROM student WHERE id = 1";
pstmt = conn.prepareStatement(sql);
// 4. 执行查询
rs = pstmt.executeQuery();
// 5. 处理结果集
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.printf("学生信息 - ID: %d, 姓名: %s", id, name);
}
} catch (SQLException e) {
System.err.println("数据库操作异常:");
e.printStackTrace();
} finally {
// 6. 释放资源(注意:连接是返回到连接池,不是真正关闭)
JDBC04.close(conn, pstmt, rs);
}
}
测试结果
三种进化的好处
第一种
基础封装:减少代码量
第二种:
配置文件:使用更加灵活
第三中:
线程池:更快,减少了重复的创建资源和释放资源的过程。