目录
前言
手写jdbc 工具类
封装思维
对于封装思维的理解
举一个关于封装思维的例子
解决硬编码
什么是硬编码?
硬编码的例子:
解决办法
解法1
解法2
解法3
jdbc工具类总的代码如下
资源关闭的先后问题
解决办法:
总结
前言
- 本篇博客,主要是为了帮助大家了解,什么是封装思维 和如何解决硬编码,以及他们的具体应用
手写jdbc 工具类
封装思维
对于封装思维的理解
我们知道"封装" 这一词,我们最早是从面向对象的三个特征:1 封装 2 继承 3 多态 。了解的。
关于封装的含义
- 隐藏数据:在Java中通过使用访问权限修饰符和 getxx方法 和setxx方法 ,阻止外界非法访问数据
- 简化代码,方便多次调用(本次手写工具类中有应用)
- 把对象的属性和行为,看成一个密不可分的整体,将两者结合起来(封装在对象中)
举一个关于封装思维的例子
最近使用jdbc 连接数据库,完成通过控制台输出方式的一个学生管理系统。主要功能就是对学生进行增删改查 操作。但我们知道,我们的每一步操作,都包含许多重复的代码(除了我们书写的sql语句和调用相关方法,为参数赋值,得到目标数据之外)
如下图所示:
加载数据库驱动(使用的数据库jar 包)
Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test666?useSSL=false","root","123");
以及 关闭各个资源
关闭的顺序为:后面使用的,先关闭
try { if(rs != null) rs.close(); if(stmt!=null) stmt.close(); if (conn!= null) conn.close(); } catch (SQLException e) { throw new RuntimeException(e); }
正是因为存在这么多相同的代码,通过理解并使用"封装思维“想着能否把相同的代码(属性,方法),按照它们的功能或目的,写在一起,集中调用。
这就是为什么我要使用jdbc 工具类的原因
解决硬编码
什么是硬编码?
我们通常把直接写在 源代码的固定的值或数据,而不是通过变量、配置文件、环境变量或函数调用等方式动态地获取这些值。
硬编码的例子:
- 在代码中直接写入数据库连接字符串、密码等敏感信息。
- 在代码中直接写入文件路径、端口号、IP 地址等配置信息。
- 在代码中直接写入固定的文本、数字或其他数据。
如下图所示:
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test666?useSSL=false","root","123");
解决办法
- 1 创建配置文件,将硬编码写在配置文件中(这里我使用的是properties 配置文件)
db.properties 配置文件
- 2 使用properties 配置文件在Java中对应的Properties类型的对象,调用load方法解析读取配置文件内容
关于Properties类 的含义
- 是properties 配置文件在Java中的一个对应类: Java。util.Properties 是Map 接口实现类,key -value 都是String类型
以下有三种解法,它们的区别,在于配置文件的加载路径不同,导致解析配置文件的方式也会有所不同。
解法1
- 要求添加的配置配置文件再当前项目下
代码如下
public class TestProperties {
public static void main(String[] args) throws IOException {
Properties props = new Properties();
// 解析配置文件
props.load(new FileInputStream("db.properties"));
// 获取数据
String driver = props.getProperty("driverClass");
System.out.println(driver);
}
}
- 发现在上面的代码中, FileInputStream("文件名“)相对路径
运行后发现:
从图片我们可以看见,找不到指定文件。
原因是当我们写入的参数为相对路径(文件名:这里是指:db.properties),是在当前项目下寻找,如果没找到,就会出现这样的错误。
当前项目路径为:
System.out.println("当前路径:"+System.getProperty("user.dir"));
FileInputStream("文件名“)相对路径,是相对与 user.dir路径:当前路径:D:\GPT浏览器下载\IDEA代码\日常代码集\project1
因此解决方法是:把配置文件移到project1 项目下
运行结果
解法2
- 要求读取的配置文件要和 引用class的这个 java文件在同一个包下,否则会报空指针异常。
代码如下
Properties props = new Properties();
// 要求读取的配置文件要和 引用class的这个 java文件在同一个包下,否则会报空指针异常
InputStream in = TestProperties.class.getResourceAsStream("db.properties");
System.out.println("当前路径:"+in);
props.load(in);
// 获取数据
String driver = props.getProperty("driverClass");
System.out.println(driver);
使用映射的方式 ,调用 TestProperties.class.getResourceAsStream("db.properties");
这里有个要求:
要求读取的配置文件要和 引用class的这个 java文件在同一个包下,否则会报空指针异常。
我们知道src文件夹下的文件都会经过编译后在classes文件夹下产生对应的字节码文件
如下图所示:
运行结果
解法3
- 使用类加载器:ClassLoader,把硬盘的文件加载到内存上,要求配置文件应该放在类路径下(classPath)
如图所示:类加载器:ClassLoader具体应用场景
代码如下
Properties props = new Properties();
// 获取类加载器:getClassLoader()
// 根据ClassLoader类加载器 获取资源(配置文件),要求资源放在类路径下 src目录下
InputStream in = TestProperties.class.getClassLoader().getResourceAsStream("db.properties");
System.out.println("当前路径:"+in);
props.load(in);
// 获取数据
String driver = props.getProperty("driverClass");
System.out.println(driver);
运行结果
在之后运行手写的jdbc工具类代码,将加载驱动(数据库jar包)路径作了修改。之前的不加cj方式,过时了。
修改之后,如图所示:
之前控制台会报错:
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
解决办法:
报错信息提示你在加载 MySQL 驱动类时使用了旧的驱动类 com.mysql.jdbc.Driver,而新的驱动类是 com.mysql.cj.jdbc.Driver。注意:
在MySQL 版本在8.0之上(包括8.0版本)驱动已经通过服务提供者接口(SPI)自动注册,因此手动加载驱动类通常是不必要的
jdbc工具类总的代码如下
- 在单线程下没有问题, 在多线程下有问题
- ctrl + alt + t 把代码使用指定模版包裹(try-catch....)
- 注意作用域问题(static 静态代码块中,在外部,无法被访问)
- 使用类加载器;ClassLoader 解析读取配置文件内容
- 可变参数( Object ... params ,可以任意控制输入数据的数量)
package it.Util;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @Author: Administrator
* @Description:
* @Date: 2024/11/8 下午2:56
* @Version: 1.0
*/
public class jdbcUtil {
// 数据库连接
/**
* jdbc的工具类
* 在单线程下没有问题, 在多线程下有问题
*/
static Properties props = new Properties();
private static Connection conn;
private static PreparedStatement stmt;
//使用静态代码块 加载数据库四大参数
static {
try {
props.load(jdbcUtil.class.getClassLoader().getResourceAsStream("db.properties"));
Class.forName(props.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 = props.getProperty("jdbcUrl");
String username = props.getProperty("username");
String password = props.getProperty("password");
return DriverManager.getConnection(jdbcUrl, username, password);
}
public static void close( 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 把代码使用指定模版包裹
try {
conn = getConnection();
stmt = conn.prepareStatement(sql);
//给问号赋值
for (int i = 0; i < params.length; i++) {
stmt.setObject(i + 1, params[i]);
}
//执行
return stmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
close( null);
}
}
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 resultSet = pstmt.executeQuery();
//执行
return resultSet;
}
}
在书写代码过程中,同样也遇到一些困难:
资源关闭的先后问题
原因:报错 No operations allowed after statement closed 表示尝试在一个已经关闭的数据库语句对象上执行操作。这通常发生在数据库连接或语句对象被关闭后,仍然尝试执行查询或更新操作
解决办法:
在写查询方法时,不允许先关闭Connection 资源,prepareStatement 资源
再返回查询结果集。而是在测试类中,当我们的操作完成之后,再关闭所有资源
测试类代码:
String sql="select *from user where userId=?"; ResultSet resultSet=null; try { resultSet= jdbcUtil.executeQuery(sql, 1); if (resultSet.next()){ String username = resultSet.getString("username"); String pwd = resultSet.getString("pwd"); System.out.println(username+"\t\t"+pwd); //System.out.println(resultSet); } } catch (Exception e) { throw new RuntimeException(e); } finally { jdbcUtil.close(resultSet); } }
运行结果:
总结
本篇博客,简单的复习了如何手写jdbc 工具类:如何理解 封装思维,解决硬编码问题 等问题