首页 > 数据库 >JDBC学习笔记(四)--JdbcUtil工具类的底层实现、解决 java.sql.SQLException: Operation not allowed after ResultSet closed

JDBC学习笔记(四)--JdbcUtil工具类的底层实现、解决 java.sql.SQLException: Operation not allowed after ResultSet closed

时间:2024-11-16 19:17:11浏览次数:3  
标签:JDBC java -- System prop int Properties sql properties

目录

(一)为什么要使用JdbcUtil工具类

(二)创建一个prorperties文件

1.在文件目录或src目录下,选择新建FIle

2.创建properties文件 

3.编写配置文件

Java基础:反射

4.获取资源的方式

第一种

第二种 

​编辑 第三种


(一)为什么要使用JdbcUtil工具类

问题:

  1. 在编写jdbc的时候,在每一句sql语句以及功能的实现时,有大量重复的代码,如获取Connection,关闭资源...
  2. 数据库四大参数,写在代码中,硬编码(后期需要连接其他的数据库时,可能需要把代码改掉,换个环境就要改代码,修改配置参数,就要就该代码,造成不便)4c7d061b44a6469a88395e7ccedd56d5.png

解决方案:

  1. 封装思维 ,解决大量代码重复问题,将一样的代码取出来,加载驱动类只需要一次就行了
  2. 配置文件:properties文件

(二)创建一个prorperties文件

1.在文件目录或src目录下,选择新建FIle

2c68c149b1af43adbab0840caf08d961.png

2.创建properties文件 

cca282144f0b428cb5b17f1113a10c78.png

3.编写配置文件

把配置参数引入到properties文件里面

4c6fb8f8f97d4c25b3696bb3cad4d4a9.png

使用Properties格式配置文件

#注释   key=value
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/esa?useUnicode=true&characterEncoding=utf8
username=root
password=123456

为什么要使用Properties? 

  • Properties文件不需要像xml一样去手动解析, jdk有一个对应类:java.util.Properties,是Map接口实现类,但是key,value都是String类型

9bd0190681964947b8a44b8101553397.png

  • Properties可以通过load方法,读取Properties文件,解析数据,变成key/value结构,存储到Properties对象

e63e3c793ccf40ae8f239dc7dd96d57f.png

1155d8ebe5ff4ac88f48112235ad8957.png

2404ebe3fb374f659b7ee75aeefff9cd.png

Java基础:反射

反射(Reflection),Java 中的反射机制是指,Java 程序在运行期间可以获取到一个对象的全部信息。

反射机制一般用来解决Java 程序运行期间,对某个实例对象一无所知的情况下,如何调用该对象内部的方法问题。

4.获取资源的方式

不同的方式对存放文件的目录有要求

第一种

放在项目下面

Properties prop=new Properties();
//读取db.properties文件
//FileInputStream("文件名"); 相对路径:相对于user.dir
//user.dir 系统参数 当前项目路径
//D:\作业\jdbc-demo1
System.out.println(System.getProperty("user.dir"));
//FileInputStream("文件名");  相对路径:相对于user.dir
prop.load(new FileInputStream("db.properties"));
//TestProperties.class.getResourceAsStream("db.properties");

System.out.println(prop.getProperty("driverClass"));

第二种 

放在同一个包下面

Properties prop=new Properties();
//读取db.properties文件
//FileInputStream("文件名"); 相对路径:相对于user.dir
//user.dir 系统参数 当前项目路径
//D:\作业\jdbc-demo1
//System.out.println(System.getProperty("user.dir"));
//FileInputStream("文件名");  相对路径:相对于user.dir
//prop.load(new FileInputStream("db.properties"));
//TestProperties.class.getResourceAsStream("db.properties");  要求:文件与这个类在同一个目录下
InputStream in=TestProperties.class.getResourceAsStream("db.properties");
System.out.println(in);
prop.load(in);
//获取数据
System.out.println(prop.getProperty("driverClass"));

报错: 

25c84184c5774f6197debf14322a1a05.png 原因:

TestProperties.class.getClassLoader().getResourceAsStream("db.properties"); 

要求:文件与这个类在同一个目录下

将这个包移动到与类同目录即可

解决成功: 

5d44730f59184e1f8adf67850fb72146.png 第三种

获取类加载器,放在src下面

 Properties prop=new Properties();
        //读取db.properties文件
        //FileInputStream("文件名"); 相对路径:相对于user.dir
        //user.dir 系统参数 当前项目路径
        //D:\作业\jdbc-demo1
        //System.out.println(System.getProperty("user.dir"));
        //FileInputStream("文件名");  相对路径:相对于user.dir
        //prop.load(new FileInputStream("db.properties"));
        //TestProperties.class.getResourceAsStream("db.properties");  要求:文件与这个类在同一个目录下
//        InputStream in=TestProperties.class.getResourceAsStream("db.properties");
//        System.out.println(in);
//        prop.load(in);
        //getClassLoader() 获取类加载器
        InputStream in=TestProperties.class.getClassLoader().getResourceAsStream("db.properties");

        System.out.println(in);
        prop.load(in);
        //获取数据
        System.out.println(prop.getProperty("driverClass"));

8b9286719b384ffdb5164132395e983d.png

38a44f57d94c4013803318a747a80219.png

(三)编写JdbcUtil工具类

1.可变参数

定义一个方法,对数组元素进行相加的方法

方法一:

       通常我们会使用for循环来进行相加,但是在main方法里,需要定义一个长度不可变,数组里面的数据也定义好了的数组。

代码实现:
public static int sum(int[] nums){
    int sum = 0;
    for (int i = 0; i < nums.length; i++) {
        sum += nums[i];
    }
    return sum;
}

public static void main(String[] args) {
    int[] arr = {1,2,3,4,5};
    int sum = sum(arr);
    System.out.println(sum);
}

控制台输出:

输出是没有问题的,但是定义一个数组,需要多几行代码,如果我们需要使用

System.out.println(sum(arr,1,2,3,4,5,6,7,8,9,10));这个语句来进行计算,是会报错的,因为在现在这个方法里面,参数是固定的,那么就要使用可变数组了。

方法二(可变参数):

java中有一个语法:

可变参数: 数据类型... 参数名  本质数组

限制:

  1. 可变参数只能是最后一个参数
  2. 一个方法只能有一个可变参数,不能有多个,如果是数组,一个方法可以有多个数组参数

代码实现:

/**
* 可变参数
*/
public class Test {
    //定义一个方法,对数组元素进行相加的方法
    //java中有一个语法: 可变参数: 数据类型... 参数名  本质数组
    //限制: 可变参数只能是最后一个参数
    //      一个方法只能有一个可变参数,不能有多个,如果是数组,一个方法可以有多个数组参数
    public static int sum(int... nums){
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        return sum;
    }

    public static void main(String[] args) {
//        int[] arr = {1,2,3,4,5};
//        int sum = sum(arr);
        System.out.println(sum(4,9,10,22,11,34));
    }
}

控制台输出:

结果正确!!!

2.执行增删改sql语句

编写JdbcUtil工具类,封装代码,减少重复代码

将获取连接以及加载数据参数的代码封装起来,执行sql语句的方法,在测试类中只需要写sql语句就行了,大大减少了重复代码

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    static Properties prop=new Properties();
    //使用静态代码块 加载数据库四大参数
    static {
        InputStream in=JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
        try {
            prop.load(in);
            Class.forName(prop.getProperty("driverClass"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取连接对象
     * @return Connection对象
     */
    public static Connection getConnection() throws SQLException {
        String jdbcUrl=prop.getProperty("jdbcUrl");
        String username=prop.getProperty("username");
        String password=prop.getProperty("password");
        return DriverManager.getConnection(jdbcUrl,username,password);
    }

    /**
     * 关闭连接对象
     */
    public static void close(Connection conn, PreparedStatement stmt, ResultSet rs){
        try{
            if (rs!=null) rs.close();
            if (stmt!=null) stmt.close();
            if (conn!=null) conn.close();
        }catch (SQLException e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行增删改的sql的方法
     */
    public static int executeUpdate(String sql,Object... params){
        //ctrl + alt + t 把代码使用指定的方法包裹起来
        Connection conn=null;
        PreparedStatement pstmt=null;
        try {
            conn=getConnection();
            pstmt= conn.prepareStatement(sql);
            for (int i=0;i<params.length;i++){
                pstmt.setObject(i+1,params[i]);
            }
            return pstmt.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            close(conn,pstmt,null);
        }

    }
}

使用测试类,检测代码能否运行成功

增删改调用executeUpdate方法

public class TestJdbcUtil {
    public static void main(String[] args) {
        //执行增删改sql
        String sql="delete from tb_user where username = ? and password = ?";
        int count=JdbcUtils.executeUpdate(sql,"zhangsan","123");
        if(count>0){
            System.out.println("删除成功");
        }else{
            System.out.println("删除失败");
        }
    }
}

运行结果:

删除成功,表中已没有zhangsan这条数据

3.执行查询语句

查询相较于增删改复杂一些,就比如在关闭资源这一块

执行代码:

public class TestJdbcUtil {
    public static void main(String[] args) {

        //执行查询sql
        String sql="select * from tb_user where username = ? and password = ?";
        ResultSet rs=null;
        try {
            rs=JdbcUtils.executeQuery(sql,"lisi","456");
            while (rs.next()){
                String username=rs.getString("username");
                String password=rs.getString("password");
                System.out.println(username+"--"+password);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            JdbcUtils.close(null,null,rs);
        }
    }
}

工具类的实现:

public static ResultSet executeQuery(String sql,Object... params) throws SQLException{
    Connection conn=null;
    PreparedStatement pstmt=null;
    conn=getConnection();
    pstmt= conn.prepareStatement(sql);
    for (int i=0;i<params.length;i++){
        pstmt.setObject(i+1,params[i]);
    }
    ResultSet rs=pstmt.executeQuery();
    close(conn,pstmt,null);
    return rs;
}

运行结果:

报错: java.sql.SQLException: Operation not allowed after ResultSet closed

因为先把conn和pstmt关闭,那么ResultSet也就没意义了,这也是为什么要倒序关闭的原因,数据会无法传过来

解决方法:

1.在JdbcUtil工具类中修改close()方法,把Connection和PreparedStatement两个属性提取出来,变成静态属性

2.将close()需要传入的参数变成只有ResultSet一个,但是方法中仍然需要关闭Connection和PreparedStatement资源

3.修改其他方法中用到的close()方法

运行:

成功!!

但是这样写只能在单线程中没有问题,在多线程中有问题,多线程在于数据共享,当线程a执行完了,把资源关闭后,那么线程b就无法使用这个资源,所以代码还需要改进

标签:JDBC,java,--,System,prop,int,Properties,sql,properties
From: https://blog.csdn.net/2301_76890677/article/details/143626430

相关文章

  • 序列化与反序列化-基本了解使用
    什么是序列化与反序列化        网络传输的数据必须是二进制数据,但调用方请求的出入参数都是对象。对象是不能直接在网络中传输的,所以我们需要提前把它转成可传输的二进制,并且要求转换算法是可逆的,这个过程我们一般叫做“序列化”。这时,服务提供方就可以正确地从二进......
  • 催化生长过程中的界面能
    界面能由边界形成能和粘附能决定,这两部分能量反映了纳米管边缘和催化剂之间的物理和化学相互作用,以及系统的稳定性来源。以下是具体原因和物理意义:1.边界形成能(EdgeFormationEnergy,)边界形成能描述的是在形成一个新的边界(例如切割碳纳米管或石墨烯边缘)时需要消耗的能量......
  • 记一次Mysql远程连接报错
    问题描述:Plugincachingsha2passwordcouldnotbeloaded:在wsl2用docker中拉取了mysql镜像,启动后想在win下的环境远程连接到docker中的mysql,报错了,报错如下所示搜寻了相关的资料发现,在拉下来的myslq版本是8.0+,caching_sha2_password是默认的身份验证插件,既然sqlyog无......
  • 设计模式已经过时了?再也不用学了?
    设计模式是高级软件开发工程师的必备技能之一。虽然设计模式本身并不是解决所有问题的万能钥匙,但掌握设计模式可以帮助开发者在以下方面显著提升能力和效率:1.设计模式的意义设计模式是一种总结了软件开发中的常见问题和解决方案的经验集合。通过学习和使用设计模式,开发......
  • 【C语言】函数递归
    1、递归的概念    其实我们在前面的学习中已经使用过函数的递归了。那么什么是递归呢    递归是一种解决问题的方法,就是函数自己调用自己,例如下面的函数。         上面就是一个简单的函数递归,在main函数内调用自己。2、递归的使用思路和......
  • 《MySQL必知必会》_9
    更新数据UPDATEcustomersSETcust_email='[email protected]'WHEREcust_id=10005;更新的表的名字为customers,SET命令用来将新值赋给被更新的列UPDATEcustomersSETcust_email='[email protected]',    cust_name='TheFudds'WHEREcust_id=10005;在更......
  • Codeforces Round 987 (Div. 2)
    好不容易的一次CF阳间赛,这次题目普遍较长。A-PenchickandModernMonument题目大意将一个非递增的序列通过操作某些数(可增大可减小),最终使得序列变成一个非递减的序列,求出最少要操作多少次?解题思路这个题也是想了不断时间,而且还提交错误一次,原因就是调试的代码没......
  • 什么?掌握 UniApp 页面路由竟如此简单!
    引言UniApp是一个跨平台的开发框架,提供了很多实用的API来帮助开发者处理跨平台的需求,尤其是页面路由相关的功能。页面路由API使得页面跳转、返回等操作变得更加简单且高效,特别适用于在小程序、H5、App等平台之间进行页面管理。本文将详细介绍UniApp提供的页面路由......
  • 熟食店称重计价秤软件下载 佳易王触摸屏称重自动读取重量自动计算金额系统操作教程
    一、概述【软件资源文件下载在文章最后】熟食店称重计价秤软件下载触摸屏称重自动读取重量自动计算金额系统操作教程1、软件可以自动读取称的重量。2、自动计算金额并累计。不需打印条形码直接称重计算,节省人力和时间。 软件同时支持称重商品和条形码百货商品支持进销存,库......
  • python文件排序都有哪些方法
    在python环境中提供两种排序方案:用库函数sorted()对字符串排序,它的对象是字符;用函数sort()对数字排序,它的对象是数字,如果读取文件的话,需要进行处理(把文件后缀名‘屏蔽’)。(1)首先:我测试的文件夹是/img/,里面的文件都是图片,如下图所示:(2)测试库函数sorted(),直接贴出代码:impor......