一、JDBC 概述
JDBC(Java DataBase Connectivity)是 Java 用于数据库访问的应用程序 API 接口,由一系列 Java 类和接口构成。它提供了统一的语法来操作多种关系型数据库,屏蔽了不同数据库操作语言的差异。这意味着开发人员无需为每种数据库编写特定的访问程序,大大提高了数据库访问的通用性和便利性。无论使用 MySQL 还是 Oracle 数据库,都可以使用相同的 JDBC 代码结构进行连接和操作,只需要更换对应的数据库驱动和连接信息。
二、Java 程序连接数据库步骤
需要获取对应数据库的 jar 包,这是数据库厂商提供的连接代码库。连接 MySQL 数据库,就需要下载 MySQL 的 JDBC 驱动 jar 包。
- 加载驱动:
- 使用
Class.forName("com.mysql.cj.jdbc.Driver");
来加载数据库驱动。这一步是将驱动类加载到 JVM 中,使得后续能够与数据库建立连接。
- 使用
- 获取连接对象:
- 构建连接字符串
String url = "jdbc:mysql://localhost:3306/mp";
,其中jdbc:mysql
是连接 MySQL 数据库的协议,localhost
表示本地数据库,3306
是 MySQL 的端口号,mp
是数据库名。 - 提供数据库账号和密码
String username = "root";
和String password = "root";
。 - 通过
Connection conn = DriverManager.getConnection(url, username, password);
获取连接对象,这个对象代表了与数据库的连接通道,后续的数据库操作都基于此连接。
- 构建连接字符串
- 获取执行 SQL 语句的对象:
- 使用
Statement st = conn.createStatement();
创建一个Statement
对象,它用于执行静态 SQL 语句并返回结果。
- 使用
- 执行 SQL 语句(以增删改为例):
- 构建 SQL 语句,如
String sql = "insert into tbl_user(uname,age,email) values('杨国峰',20,'110@qq.com')";
。 - 执行 SQL 语句并获取影响的行数
int row = st.executeUpdate(sql);
,对于增删改操作,该方法返回受影响的记录数。
- 构建 SQL 语句,如
- 关闭资源:
- 按照先关闭执行 SQL 的对象,再关闭连接对象的顺序关闭资源,即
st.close();
和conn.close();
。关闭资源是为了释放数据库连接和相关对象占用的系统资源,避免资源浪费和潜在的内存泄漏问题。
- 按照先关闭执行 SQL 的对象,再关闭连接对象的顺序关闭资源,即
三、JDBC 常犯错误
- 驱动类写错:例如写错驱动类的包名或类名,这会导致无法加载驱动,进而无法建立数据库连接。
- 没有构建 jar 包(add lib):如果没有将数据库的 JDBC 驱动 jar 包正确添加到项目的依赖中,程序在运行时会找不到驱动类,导致连接失败。
四、CRUD 操作
(一)查询操作(queryAll)
- 加载驱动:同连接数据库步骤中的加载驱动。
- 获取连接对象:构建连接字符串并提供账号密码获取连接。
- 构建查询 SQL 语句:如
String sql = "select uid,uname,age,email from tbl_user";
。 - 获取执行 SQL 语句的对象并执行查询:
- 使用
Statement st = conn.createStatement();
。 - 通过
ResultSet rs = st.executeQuery(sql);
执行查询语句,将查询结果封装到ResultSet
类中。
- 使用
- 遍历结果集:
- 使用
while(rs.next())
循环遍历结果集,rs.next()
方法判断下一条记录是否存在,如果存在则将指针移到下一行。 - 通过
rs.getInt("uid")
、rs.getString("uname")
等方法获取当前行指定列的值并进行输出展示。
- 使用
- 关闭资源:依次关闭结果集、执行 SQL 的对象和连接对象。
(二)修改操作(update)
- 加载驱动、获取连接对象:与上述步骤相同。
- 构建修改 SQL 语句:例如
String sql = "update tbl_user set uname='"+name+"',age="+age+",email='"+email+"' where uid="+id;
(这里的name
、age
、email
、id
是方法参数)。 - 获取执行 SQL 语句的对象并执行修改:
Statement st = conn.createStatement();
。int row = st.executeUpdate(sql);
执行修改语句并获取受影响的行数。
- 关闭资源:先关闭执行 SQL 的对象,再关闭连接对象。
(三)删除操作(delete)
- 加载驱动、获取连接对象:同前。
- 构建删除 SQL 语句:如
String sql = "delete from tbl_user where uid="+id;
(id
为方法参数)。 - 获取执行 SQL 语句的对象并执行删除:
Statement st = conn.createStatement();
。int row = st.executeUpdate(sql);
执行删除语句并获取受影响的行数。
- 关闭资源:先关闭执行 SQL 的对象,再关闭连接对象。
(四)插入操作(insert)
- 加载驱动、获取连接对象:按照标准流程。
- 构建插入 SQL 语句:如
String sql = "insert into tbl_user(uname,age,email) values('"+name+"',"+age+",'"+email+"')";
(name
、age
、email
是方法参数)。 - 获取执行 SQL 语句的对象并执行插入:
Statement st = conn.createStatement();
。int row = st.executeUpdate(sql);
执行插入语句并获取受影响的行数。
- 关闭资源:先关闭执行 SQL 的对象,再关闭连接对象。
五、Statement 存在的 SQL 注入安全问题
在使用 Statement
执行 SQL 语句进行登录验证等操作时,例如:
String sql = "select * from tbl_user where uname='" + name + "' and pwd='" + pwd + "'";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
如果恶意用户在密码输入框输入 xXXXXX' or '1'='1
,那么生成的 SQL 语句就会变成 select * from tbl_user where uname='[用户名]' and pwd='xXXXXX' or '1'='1'
,由于 '1'='1'
恒成立,所以无论用户名和密码是否正确,都会登录成功,这就是 SQL 注入安全问题。
六、使用 PreparedStatement 解决 SQL 注入安全问题
- 构建 SQL 语句时使用占位符:
- 例如
String sql = "select * from tbl_user where uname=? and pwd=?";
,这里的?
就是占位符。
- 例如
- 获取
PreparedStatement
对象:- 通过
PreparedStatement ps = conn.prepareStatement(sql);
获取。
- 通过
- 为占位符赋值:
- 使用
ps.setObject(1, name);
和ps.setObject(2, pwd);
分别为占位符赋值,避免了 SQL 拼接带来的安全隐患。
- 使用
- 执行 SQL 语句并处理结果:
ResultSet rs = ps.executeQuery();
执行查询语句,后续处理结果集的方式与使用Statement
类似。
- 关闭资源:关闭结果集、
PreparedStatement
对象和连接对象。
七、异常处理方式 try - catch - finally
在使用 JDBC 操作数据库时,可能会出现各种异常,如 ClassNotFoundException
(加载驱动类失败)、SQLException
(数据库操作异常)等。使用 try - catch - finally
块可以有效地捕获和处理这些异常,并确保资源能够正确关闭。例如:
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
try {
// 数据库操作代码
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (rs!= null) {
rs.close();
}
if (ps!= null) {
ps.close();
}
if (conn!= null) {
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
在 try
块中放置数据库操作代码,如加载驱动、获取连接、执行 SQL 语句等。如果出现异常,在 catch
块中打印异常信息。在 finally
块中,无论是否发生异常,都尝试关闭资源,以保证资源的正确释放。如果关闭资源过程中出现 SQLException
,则抛出运行时异常,以便在更高层次处理或记录日志。这种异常处理方式可以提高程序的稳定性和可靠性,避免因数据库操作异常导致程序崩溃或资源泄漏。