JDBC使用优化以及工具类封装

jdbc工具类封装v1.0

我们封装一个工具类,内部包含连接池对象,同时对外提供连接的方法和回收连接的方法!

外部配置文件

位置: src/mian/resources/druid.properties

# key = value => java Properties读取(key | value)
# druid配置的key固定命名
# druid连接池需要的配置参数,key固定命名

driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
url=jdbc:mysql://localhost:13306/huan

工具类代码

package com.jdbc.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author : Aurora
 * @version : 1.0
 * <p>
 * 建议:工具类中的方法,推荐写成静态,外部调用会更加方便!
 * 实现:属性 连接池对象[实例化一次]
 * 单例模式
 * static{
 * 全局调用一次
 * }
 * 方法:对外提供连接的方法。回收外部传入连接方法
 * @Date : 2023/1/5
 * @Describe : 内部包含一个连接池对象,并且对外提供获取连接和回收连接的方法!
 */
public class JdbcUtils {

    private static DataSource dataSource = null;

    static {
        //初始化连接池对象
        Properties properties = new Properties();
        InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");

        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            //运行时异常
            throw new RuntimeException(e);
        }

        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 对外提供连接的方法
     */
    public static Connection getConnection() throws SQLException {
        //这么写,不能保证同一个线程,两次getConnection()得到的是同一个Connection对象
        //如果不能保证是同一个连接对象,就无法保证事务的管理
        return dataSource.getConnection();
    }

    public static void freeConnection(Connection connection) throws SQLException {
        connection.close();
    }
}

jdbc工具类封装v.2.0

优化工具类v1.0版本,考虑事务的情况下!如何一个线程的不同方法获取同一个连接!

ThreadLocal的介绍

JDK 1.2的版本中就提供java.lang.ThreadLocal,为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。通常用来在在多线程中管理共享数据库连接、Session等

ThreadLocal用于保存某个线程共享变量,原因是在Java中,每一个线程对象中都有一个ThreadLocalMap<ThreadLocal, Object>,其key就是一个ThreadLocal,而Object即为该线程的共享变量。而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。

1、ThreadLocal对象.get: 获取ThreadLocal中当前线程共享变量的值。

2、ThreadLocal对象.set: 设置ThreadLocal中当前线程共享变量的值。

3、ThreadLocal对象.remove: 移除ThreadLocal中当前线程共享变量的值。

v2.0版本工具类

package com.jdbc.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author : Aurora
 * @version : 2.0
 * <p>
 * 建议:工具类中的方法,推荐写成静态,外部调用会更加方便!
 * 实现:属性 连接池对象[实例化一次]
 * 单例模式
 * static{
 * 全局调用一次
 * }
 * 方法:对外提供连接的方法。回收外部传入连接方法
 * @Date : 2023/1/5
 * @Describe : 内部包含一个连接池对象,并且对外提供获取连接和回收连接的方法!
 * <p>
 * <p>
 * 利用线程本地变量,存储连接信息,确保一个线程的多个方法可以获取同一个connection!
 * 优势:事务操作的时候 service 和 dao 属于同一个线程,不同再传递参数了
 * 大家都建议调用getConnection() 自动获取的是相同的连接池
 */
public class JdbcUtilsV2 {

    //连接池对象
    private static DataSource dataSource = null;

    private static ThreadLocal<Connection> t1 = new ThreadLocal<>();

    static {
        //初始化连接池对象
        Properties properties = new Properties();
        InputStream inputStream = JdbcUtilsV2.class.getClassLoader().getResourceAsStream("druid.properties");

        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            //运行时异常
            throw new RuntimeException(e);
        }

        try {
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

    /**
     * 对外提供连接的方法
     */
    public static Connection getConnection() throws SQLException {
        //这么写,不能保证同一个线程,两次getConnection()得到的是同一个Connection对象
        //如果不能保证是同一个连接对象,就无法保证事务的管理

        //线程本地变量中是否存在
        Connection connection = t1.get();

        //第一次没有
        if (connection == null) {
            //线程本地变量没有,那就从连接池里获取
            connection = dataSource.getConnection();
            t1.set(connection);
        }

        return connection;
    }

    public static void freeConnection() throws SQLException {

        Connection connection = t1.get();
        if (connection != null) {
            t1.remove(); //清空线程本地变量数据
            connection.setAutoCommit(true); // 事务回滚状态
            connection.close(); //线程池的连接,调用close就是回收
        }
    }
}

高级应用层封装BaseDao

基本上每一个数据表都应该有一个对应的DAO接口及其实现类,发现对所有表的操作(增、删、改、查)代码重复度很高,所以可以抽取公共代码,给这些DAO的实现类可以抽取一个公共的父类,我们称为BaseDao

工具类代码

package com.jdbc.utils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author : Aurora
 * @Date : 2023/1/5
 * @Describe : 封装dao数据库重复代码
 * <p>
 * 封装两个方法:
 * 1,简化DQL
 * 2,简化非DQL
 */
public abstract class BaseDao<T> {

    /**
     * 封装简化非DQL语句
     *
     * @param sql    带占位符的sql语句
     * @param params 占位符的值
     * @return 执行影响的行数
     */
    public int executeUpdate(String sql, Object... params) throws SQLException {

        //获取连接
        Connection connection = JdbcUtilsV2.getConnection();

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //可变参数可以当数组使用
        for (int i = 1; i <= params.length; i++) {
            preparedStatement.setObject(i, params[i - 1]);
        }

        //发送sql语句
        int rows = preparedStatement.executeUpdate();

        preparedStatement.close();

        //connection.close(); //不一定合适

        //是否回收连接,需要考虑是不是事务
        if (connection.getAutoCommit()) {
            //没有开启事务

            //没有开启事务,正常回收连接
            JdbcUtilsV2.freeConnection();
        }

        //开启事务了,不需要连接,业务层处理
        //connection.setAutoCommit(false);

        return rows;
    }

    /**
     * 非DQL语句封装方法 -> 返回值 固定为 int
     * DQL语句封装方法 -> 返回值,是什么类型呢?
     * 并不是list<Map> map key和value自定义!不用先设定好!
     * map没有数据校验机制,不支持反射操作
     * <p>
     * 数据库数据 -> Java的实体类
     * 表中一行 -> Java一个实体类 -> 多行 -> List<Java实体类> list;
     * <p>
     * DQL -> List<Map> -> 一行 -> map -> List<Map>
     * <T> 声明一个泛型,不确定类型
     * 1.确定泛型 User.class T = User
     * 2.要使用反射技术属性赋值
     * public <T> List<T> executeQuery(Class<T> clazz,String sql,Object... params);
     */

    /**
     * 将查询结果封装到一个实体类集合
     *
     * @param clazz  要接值的实体类集合的模板对象
     * @param sql    查询语句,要求列名或者别名等于实体类的属性名! u_id as Uid => Uid
     * @param params 占位符的值,要和 ?位置对象,进行传递
     * @param <T>    声明结果的类型
     * @return 查询类型集合
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     */
    public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException {

        //获取连接
        Connection connection = JdbcUtilsV2.getConnection();

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        if (params == null && params.length != 0) {
            for (int i = 1; i <= params.length; i++) {
                preparedStatement.setObject(i, params[i - 1]);
            }
        }

        //发送sql语句
        ResultSet resultSet = preparedStatement.executeQuery();

        //结果集解析
        List<T> list = new ArrayList<>();

        //获取列信息对象
        ResultSetMetaData metaData = resultSet.getMetaData();

        //有了它之后,可以水平遍历列!
        int columnCount = metaData.getColumnCount();

        while (resultSet.next()) {

            //一行数据对应一个 T 类型的数据
            T r = clazz.newInstance(); //调用类的无参构造函数实例化对象!

            //自动遍历列,注意,要从1开始,并且小于等于总列数!
            for (int i = 0; i <= columnCount; i++) {

                //对象的属性值
                Object value = resultSet.getObject(i);

                //获取指定下角标的列的名称!resultSetMetaData

                //getColumnLabel:会获取别名,如果没有别名才是列的名称
                String propertyName = metaData.getColumnLabel(i);

                //反射,给对象属性赋值
                Field field = clazz.getDeclaredField(propertyName);
                field.setAccessible(true); //属性可以设置,打破private的修饰限制

                /**
                 * 参数1:要赋值的对象,如果属性是静态,第一个参数可以为null
                 * 参数2:具体的属性值
                 */
                field.set(r, value);
            }

            list.add(r);
        }

        //关闭资源
        resultSet.close();
        preparedStatement.close();

        if (connection.getAutoCommit()) {
            //没有事务,可以关闭
            JdbcUtilsV2.freeConnection();
        }

        return list;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了JDBC工具类代码实现,其中使用了ResourceBundle进行资源的绑定,主要是绑定到jdbc.properties文件,方便读取相关配置信息。工具类代码实现包括加载驱动、关闭资源、建立数据库连接、执行预编译的SQL语句、处理结果以及释放资源等步骤。\[1\] 引用\[2\]中提到了在src根目录下创建了一个db.properties文件,其中包含了数据库的相关配置信息,如用户名、密码、驱动名和URL等。\[2\] 引用\[3\]中给出了一个c3p0-config.xml配置文件的示例,其中包含了默认的连接池配置信息,包括用户名、密码、驱动类、JDBC URL以及连接池的参数配置等。\[3\] 综合以上引用内容,可以得出jdbc工具类的最终版应该包括以下几个步骤: 1. 加载驱动:使用Class.forName()方法加载数据库驱动。 2. 建立数据库连接:读取jdbc.properties文件或者db.properties文件中的配置信息,包括用户名、密码、驱动名和URL等,使用DriverManager.getConnection()方法建立数据库连接。 3. 执行SQL语句:根据具体需求,编写SQL语句,并使用PreparedStatement进行预编译。 4. 处理结果:根据SQL语句的类型,使用ResultSet获取查询结果或者使用executeUpdate()方法执行更新操作。 5. 释放资源:关闭ResultSet、PreparedStatement和Connection等资源,释放数据库连接。 以上是一个简单的jdbc工具类的最终版实现,具体的代码实现可以根据具体需求进行调整和扩展。 #### 引用[.reference_title] - *1* [JDBC工具类 以及使用JDBC工具类 一般开发不可能直接使用 JDBC 了解即可](https://blog.csdn.net/qq_40417070/article/details/121900909)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [JDBC封装工具类操作数据库(终极版)](https://blog.csdn.net/m0_57606273/article/details/120922107)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [JavaWeb基础之JdbcUtils工具类final](https://blog.csdn.net/weixin_30727835/article/details/96386006)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值