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;
    }

    @Override
    public 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.编写sql
            String 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.编写sql
            String 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张图片

总结

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

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

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

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

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

你可能感兴趣的:(数据库,java)