实现JDBC简单封装

实现JDBC简单封装

JDBCUtil类可以较方便地执行常用的DQL、DML、DLL语句。

package base.jdbc.util;

import java.io.FileInputStream;
import java.sql.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Properties;

/**
 * @author QX
 * JDBC简易封装类
 */
public class JDBCUtil {
    private String[] queryFields = null;  // 所有的字段
    private  Connection connection = null; // 数据库连接对象
    private static String url = null;
    private static String user = null;
    private static String password = null;

    /**
     *
     * @param queryFields 设置待查询的字段
     */
    public void setQueryFields(String[] queryFields) {
        this.queryFields = queryFields;

    }

    static {
        try {
            Properties properties = new Properties();
            properties.load(new FileInputStream("src\db.properties"));
            String driver = properties.getProperty("driver");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            url = properties.getProperty("url");
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    JDBCUtil(String[] queryFields) throws SQLException {
        this.queryFields = queryFields;
        this.connection = DriverManager.getConnection(url, user, password);
    }

    private PreparedStatement getPreStatement(String sql, Object... params) {
        int cnt = 0;
        for (int i = 0; i < sql.length(); i++) {
            if (sql.charAt(i) == '?') {
                cnt++;
            }
        }
        if (cnt != params.length || params.length > this.queryFields.length) {
            return null;
        }

        PreparedStatement preStatement = null; // 预编译sql执行对象
        try {
            preStatement = this.connection.prepareStatement(sql);
            for (int i = 1; i <= cnt; i++) {
                preStatement.setObject(i, params[i - 1]);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return preStatement;
    }

    /**
     *
     * @param sql 待执行的sql语句
     * @param params ?对应的参数
     * @return  更新数据的总数
     * @throws SQLException SQL执行异常
     */
    public int update(String sql, Object... params) throws SQLException {
        PreparedStatement preStatement = getPreStatement(sql, params);
        assert preStatement != null;
        int ret = preStatement.executeUpdate();
        release(preStatement, null);
        return ret;
    }


    /**
     *
     * @param sql 待执行的sql语句
     * @param params ?对应的参数
     * @return 查询的结果
     * @throws SQLException SQL执行异常
     */
    public LinkedList<HashMap<String, Object>> query(String sql, Object... params) throws SQLException {
        PreparedStatement preStatement = getPreStatement(sql, params);
        assert preStatement != null;
        ResultSet resultSet = preStatement.executeQuery();
        LinkedList<HashMap<String, Object>> allRecords = new LinkedList<>();
        // LinkedHashMap为有序集合,可以保证记录的顺序,与数据库的显示一致
        while (resultSet.next()) {
            HashMap<String, Object> oneRecordMap = new LinkedHashMap<>();  // 一条记录
            for (String field : this.queryFields) {
                Object value  = resultSet.getObject(field);
                oneRecordMap.put(field, value);
            }
            allRecords.add(oneRecordMap);
        }
        release(preStatement, resultSet);
        return allRecords;
    }

    /**
     * 开启事务
     */
    public void setAutoCommit(boolean isAuto){
        try{
            if(connection != null && !connection.isClosed()){
                connection.setAutoCommit(isAuto);
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * 提交
     */
    public void commit(){
        try{
            if(connection != null && !connection.isClosed()){
                connection.commit();
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * 回滚
     */
    public void rollback(){
        try{
            if(connection != null && !connection.isClosed()){
                connection.rollback();
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * 释放资源
     */
    private void release(PreparedStatement pre, ResultSet res){
        try{
            if(pre != null){
                pre.close();
            }
            if(res != null){
                res.close();
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * 关闭连接
     */
    public void close() {
        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

这里为什么要用PreparedStatement,而不是Statement,有两个原因:

  • 提高性能

    • 它可以使用占位符?,是预编译的,批处理比Statement效率高
  • 可防止SQL注入攻击

    • 所谓SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。例如登录时执行这样一条sql语句:select * from t_login where username='xx' and password='xx' or 1=1,无论用户名和密码是什么,后面的1=1始终成立,从而获取所有用户的账号密码

测试类

package base.jdbc.util;

import org.junit.Test;

import java.util.HashMap;
import java.util.LinkedList;

public class UtilTest {
    @Test
    public void queryTest() {
        JDBCUtil demo = null;
        try {
            demo = new JDBCUtil(new String[]{"id", "name", "age", "sex", "money", "dpt_id", "phone", "update_time"});
            demo.setAutoCommit(false);
            demo.setQueryFields(new String[]{"id", "name", "age", "sex", "money", "dpt_id"}); // 设置要查询的字段
            String sql = "select * from user where id < ? and sex = ?";
            LinkedList<HashMap<String, Object>> result = demo.query(sql, 7, "男");
            for (var oneRecordMap : result) {
                System.out.println(oneRecordMap);
            }
            demo.commit();
        } catch (Exception e) {
            demo.rollback(); 
            e.printStackTrace();
        }finally {
            demo.close();
        }
    }
    @Test
    public void updateTest() {
        JDBCUtil demo = null;
        try {
            demo = new JDBCUtil(new String[]{"id", "name", "age", "sex", "money", "dpt_id", "phone", "update_time"});
            demo.setAutoCommit(false);
            String sql = "update user set dpt_id = ?, money = ?";
            System.out.println(demo.update(sql, 2, 50.8));
            demo.commit();  
        } catch (Exception e) {
            demo.rollback();
            e.printStackTrace();
        }finally {
            demo.close();
        }
    }
}

原数据库表为

image-20200607101701757查询结果为:

image-20200607095535649

更新结果为:

image-20200607101904961

money、dpt_id字段已更新。

该类的缺点是,调用close()后,系统将回收数据库连接对象的资源,频繁地申请连接对象,将消耗资源,执行效率不高,于是便有了数据库连接池,所谓连接池,就是提前申请了多个连接池对象资源,将它们放入某个容器中,当需要时,从容器中拿出供使用,使用完后,调用封装好的close(),并不会将资源归还系统,而是再将它放回容器中(非常类似于多线程池)。使用较广泛数据库连接池的有c3p0druid(阿里开发),druid相对于c3p0使用更简单,效率更高。

原文地址:https://www.cnblogs.com/main404/p/13056552.html