java基础之JDBC
简介
什么是JDBC?
JDBC指java连接数据库,是一种标准java应用编程接口(JAVA API),用来连接java编程语言和广泛的数据库,其实就是java语言操作数据库。
JDBC本质:
是官方(sun公司)定义的一套操作所有关系型数据库的规则。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC编程),真正执行的代码是驱动jar包中的实现类。
入门
那么我们如何利用JDBC进行操作数据库呢?
一般步骤分为以下几步:
- 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
- 复制jar包到项目的libs目录下
- 右键–> Add As Library
- 注册驱动
- 获取数据库连接对象 Connection
- 定义sql语句
- 获取执行sql语句对象 Statament或PreparedStatement(防止sql注入问题)
- 执行sql语句
- 处理执行之后的结果
- 释放资源
练习
修改account表中的记录
将account表中所有人的钱数都修改为100
这是执行sql语句之前account表的状态:
我们执行以下代码:
/*
这个sql语句是将所有人的钱数修改为100
*/
public static void main(String[] args) throws Exception {
// 1. 导入驱动jar包
// 2. 注册驱动(不写也行)
Class.forName("com.mysql.jdbc.Driver"); //这句话的意思是注册的是mysql的驱动
// 3. 获取数据库连接对象
//三个参数分别是位置、用户名、密码
//在第一个参数里表示连接的是mysql数据库ip地址(locdlhost代表本机)3306端口db3数据库,因为一般我们连接的都是本机,所 //以我们一般将本机的ip地址和端口号省略,省略后的语句在下一行
//Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
Connection con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
// 4. 定义sql语句
String sql = "update account set balance = 100";
// 5. 获取执行sql的对象statemen
Statement sta = con.createStatement(); //执行对象的类型为Statement,是用来执行sql语句的
// 6. 执行sql
//如果是数据库的添加,修改,删除,DDL用的都是executeUpdate方法,返回的参数代表影响的行数,我们可以用这个参数判断 //sql语句是否执行成功
int result = sta.executeUpdate(sql);
// 7. 处理结果
System.out.println(result);
// 8. 释放资源
sta.close(); //先释放后开启资源
con.close(); //再释放先开启的资源
}
/*
其实这个代码并不健壮,有很多的问题,比如:
数据库连接对象和执行sql对象在释放资源的时候可能会出现空指针异常等等
这些问题我们会在下面进行解决
*/
这是执行sql语句之后account表的状态:
我们发现表的更新完全正确,这也代表着我们操作的正确性。
向account表中插入一条记录
向account表中插入一条记录
这是执行sql语句之前表的状态:
我们执行以下代码:
/*
下面的jdbc操作流程我们在第一次执行的流程上进行了优化:
首先,对于异常,我门并没有把它抛出去,而是抓起来进行了处理;
其次,对于Connection对象和Statement对象,我们进行了
避免空指针异常的处理
*/
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2. 定义sql语句
String sql = "insert into account values(null, '王五', 3000)";
//3. 获取数据库连接对象
con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
//4. 获取执行sql对象
sta = con.createStatement();
//6. 执行sql
int result = sta.executeUpdate(sql);
//7. 处理结果
System.out.println(result);
if(result > 0){
System.out.println("执行成功");
}else{
System.out.println("执行失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if(sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
这是执行sql语句之后表的状态:
我们发现,执行之后结果正确,说明我们的代码是正确的。
修改account表中的记录
将所有人的钱数都修改为5000
这是执行sql语句之前表的状态:
我们执行以下的代码:
package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
//account表 修改记录
public class JDBCdemo3 {
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 定义sql语句
String sql = "update account set balance = 5000";
// 3. 获取数据库连接对象
con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
// 4. 获取执行sql对象
sta = con.createStatement();
// 5. 执行sql语句并返回结果
int result = sta.executeUpdate(sql);
// 6. 处理结果
System.out.println(result);
if(result > 0){
System.out.println("执行成功");
}else{
System.out.println("执行失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
// 7. 释放资源
if(sta != null){ //防止空指针异常
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
/*
这个代码其实是对第一个sql语句大的完善,避免释放资源时的空指针异常
*/
执行sql语句之后,结果如下:
删除account表中的一条记录
删除account表中id = 4 的记录
执行sql语句之前account表的状态:
我们执行以下代码:
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
// 1. 注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
// 2. 定义sql语句
String sql = "delete from account where id = 3";
// 3. 获取数据库连接对象
con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
// 4. 获取执行sql对象
sta = con.createStatement();
// 5. 执行sql并返回结果
int result = sta.executeUpdate(sql);
// 6. 处理结果
System.out.println(result);
if(result > 0) System.out.println("执行成功");
else{
System.out.println("执行失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//释放资源
if(sta != null){ //防止空指针异常
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){ //防止空指针异常
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
执行sql语句之后结果如下:
从表中记录可以看出我们执行的结果是正确的。
执行account的DDL语句
上面我们执行了数据库的添加、删除、修改,接下来我们说一下DDL(与数据库中定义相关的内容)语句。
account位于db3数据库,现在我们在db3数据库里再建一个student表
这是执行sql语句之前account表的状态,我们可以发现并没有student表
下面我们执行以下代码:
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 定义sql语句
String sql = "create table student(id int, name varchar(10))";
// 3. 获取数据库连接对象
con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
// 4. 获取执行sql对象
sta = con.createStatement();
// 5. 执行sql并返回结果
int result = sta.executeUpdate(sql); //返回的其实是影响的行数
// 6. 处理结果
System.out.println(result);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
// 7. 释放资源
if(sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
执行sql语句之后数据库db3的状态:
我们可以发现,student被创建了,也说明我们的代码是正确的。
详解各个对象
DriverManager
DriverManager:驱动管理对象
功能
-
注册驱动
看到这里大家可能会有疑问,会说,我们注册驱动的语句不是下面这条语句吗,和DriverManager有什么关系呢?大家请往下看
Class.forName("com.mysql.jdbc.Driver");
我们知道Class.forName()会将指定的类加载到内存中去,那我们去看看Driver的源码吧,这是Driver类的源码
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); //注册驱动 } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
我们可以发现,这里面有一个静态代码块,里面是DriverManager进行注册驱动操作的,看到这里,大家应该明白了,虽然我们写的是 Class.forName("com.mysql.jdbc.Driver"); 但其实真正注册驱动的其实是DriverManager类,其实mysql5之后的驱动jar包可以省略注册驱动的步骤。 但是还是建议写上注册驱动,以为可以向下兼容(使用低版本的mysql依然可以使用同样的代码)
下面我们来说DriverManager类的第二个功能
-
获取数据库连接
方法:
public static Connection getConnection(String url, String user, String password)
我们可以看到,这是DriverManager的静态方法,提供三个参数,分别是url、user、password
参数:
- url:指定连接的路径
- 语法: jdbc:mysql://ip地址(域名):端口号/数据库名称
- 例子:jdbc:mysql://localhost:3306/db3
- 细节:如果数据库连接的url是本主机,并且端口号是3306,则url可以写成jdbc:mysql:///数据库名称
- user:用户名
- password:密码
- url:指定连接的路径
Connection
Connection对象:数据库连接对象
功能:
- 获取执行sql的对象
- Statement createStatement()
- PreparedStatement prepareStatement(String sql))
- 管理事务
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
- 提交事务:commit()
- 回滚:rollback()
Statement
Statement:执行sql的对象
功能:
- 执行sql
- boolean execute(String sql) : 可以执行任意的sql (了解)
- int executeUpdate(String sql):执行DML语句(insert、delete、update)、DQL语句(caeate、drop、alter)
- 返回值:影响的行数,可以通过这个值判断执行结果是否成功。影响的行数大于0,执行成功,反之,执行失败。
- ResultSet executeQuery(String sql):执行DQL语句(select)
ResultSet
ResultSet:结果集对象,封装查询结果
方法
-
boolean next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果没数据返回false,否则返回true
-
getXxx(参数):获取数据
-
Xxx:代表数据类型 如: int getInt(), String getString()
-
参数:
- int:代表列的编号,从1开始,例如:getString(1)
- String:代表列的名称,例如:getDouble(“balance”)
-
使用步骤:
- 游标向下移动一行
- 判断当前行是否有数据
- 获取数据
//循环判断游标是否是最后一行末尾 while (re.next()){ //判断当前行先向下移动一行,然后判断是不是不是最后一行,不是最后一行返回true,是最后一行返回false int id = re.getInt(1); String name = re.getString("name"); double money = re.getDouble(3); System.out.println(id + "--" + name + "--" + money); }
-
下面我们来练习一下ResultSet的使用:
打印account表中的全部记录
这是执行sql语句之前account表中的记录
我们执行如下代码:
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
ResultSet re = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 定义sql语句
String sql = "select * from account";
// 3. 获取数据库连接对象
con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
// 4. 获取执行sql对象
sta = con.createStatement();
// 5. 执行sql并返回结果
re = sta.executeQuery(sql); //返回的其实是影响的行数
// 6. 处理结果
while (re.next()){ //判断当前行先向下移动一行,然后判断是不是不是最后一行,不是最后一行返回true,是最后一行返回false
int id = re.getInt(1);
String name = re.getString("name"); //获取当前游标指向行的那一列名字为name的那一个数据,类型为String
double money = re.getDouble(3); //获取当前游标指向行的第三列的那一个数据,类型为Double
System.out.println(id + "--" + name + "--" + money);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
// 7. 释放资源
if(re != null){
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
执行之后输出如下:
我们发现,确实完全的打印出来了。
接下来我们来进行一个小练习
定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,并返回
这是我们定义的Emp类,里面的成员变量分别对应于emp表中的每一个属性:
public class Emp {
private int id;
private String ename;
private int job_id;
private int mgr;
private Date joindate;
private double salary;
private double bonus;
private int dept_id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getJob_id() {
return job_id;
}
public void setJob_id(int job_id) {
this.job_id = job_id;
}
public int getMgr() {
return mgr;
}
public void setMgr(int mgr) {
this.mgr = mgr;
}
public Date getJoindate() {
return joindate;
}
public void setJoindate(Date joindate) {
this.joindate = joindate;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public int getDept_id() {
return dept_id;
}
public void setDept_id(int dept_id) {
this.dept_id = dept_id;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", ename='" + ename + '\'' +
", job_id=" + job_id +
", mgr=" + mgr +
", joindate=" + joindate +
", salary=" + salary +
", bonus=" + bonus +
", dept_id=" + dept_id +
'}';
}
}
这是我们写的方法:
public static void main(String[] args) {
List<Emp> list = new JDBCdemo8().findAll();
System.out.println(list.size());
list.forEach(System.out::println); //lambda表达式遍历输出
}
public List<Emp> findAll(){
Connection con = null;
Statement sta = null;
ResultSet re = null;
List<Emp> list = new ArrayList<Emp>();
Emp emp = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 定义sql语句
String sql = "select * from emp";
// 3. 获取数据库连接对象
con = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
// 4. 获取sql执行对象
sta = con.createStatement();
// 5. 执行sql语句并返回结果
re = sta.executeQuery(sql);
while(re.next()){
int id = re.getInt("id");
String ename = re.getString("ename");
int job_id = re.getInt("job_id");
int mgr = re.getInt("mgr");
Date joindate = re.getDate("joindate");
double salary = re.getDouble("salary");
double bonus = re.getDouble("bonus");
int dept_id = re.getInt("dept_id");
emp = new Emp();
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
list.add(emp);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
if(re != null){
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
输出结果如下:
好的,我们发现表中的记录已经全部打印了
PreparedStatement
PreparedStatement:执行sql的对象,与Statement不同的是它已经预编译过了,预编译的意思是它是动态sql,不是拼接而成的。
-
sql注入问题:在拼接sql时,有一些特殊的关键字会参与字符串的拼接,会造成安全性问题。
-
我们输入用户名和密码,和一个表中存在的用户名和密码比较,检查用户名和密码是否正确。
这是我们的用户名和密码表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0m10ro6v-1649203053145)(sql15.png)]
-
例如:我们输入 用户名:abc, 密码:a’ and ’ a ’ = ’ a
-
sql:select * from user where username = ‘abc’ and password = ‘a ’ or ’ a ’ = ’ a’
我们的代码如下:
public static void main(String[] args) { Scanner in = new Scanner(System.in); // 1. 键盘录入,判断用户名和密码 String user = in.nextLine(); String password = in.nextLine(); // 2. 调用方法 boolean value = new JDBCdemo10().login2(user, password); // 3. 判断结果 if(value){ System.out.println("登陆成功"); }else { System.out.println("登陆失败"); } } //使用Statement实现 public boolean login(String username, String password){ if(username == null || password == null){ return false; } Connection con = null; Statement sta = null; ResultSet re = null; try { // 1. 获取数据库连接对象 con = JDBCUtils.getConnection(); // 2. 定义sql语句 String sql = "select * from user where username = '"+username+"' and password = '"+password+"' "; System.out.println(sql); // 3. 获取执行sql对象 sta = con.createStatement(); // 4. 执行sql并返回结果 re = sta.executeQuery(sql); // 5. 处理结果 return re.next(); } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.close(re, sta, con); } return false; } public boolean login2(String username, String password){ if(username == null || password == null){ return false; } Connection con = null; Statement sta = null; ResultSet re = null; try { // 1. 获取数据库连接对象 con = JDBCUtils.getConnection(); // 2. 定义sql语句 String sql = "select * from user where username = '" + username +"' and password = '"+ password+"' "; // 3. 获取执行sql对象 sta = con.createStatement(); //pre.setString(1, username); //pre.setString(2, password); System.out.println(sql); // 4. 执行sql并返回结果 re = sta.executeQuery(sql); // 5. 处理结果 return re.next(); } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.close(re, sta, con); } return false; } /* 注意:这里用到了JDBCUtils类,我们会在下面讲解这个工具类 */
结果如下:
从上面的例子中我们可以看出,我们利用静态sql,也就是字符串拼接执行sql语句的方法可能会产生安全性问题(我们称为 sql注入)。那么我们如何解决这种安全性问题呢?这就需要用到PreparedStatement类了,下面我们来了解一下它的使用
-
-
-
解决sql注入:使用PreparedStatement类
-
参数使用?作为占位符。例如:
String sql = "select * from user where username = ? and password = ? ";
-
步骤:
-
导入驱动jar包
-
注册驱动
-
获取数据库连接对象Connection
-
定义sql
注意:sql的参数使用?作为占位符。如:select * from user where username = ? and password = ?
-
获取执行sql的对象PreparedStatement pre = con.prepareStatement(String sql);
-
给?赋值
- 方法:setXxx(参数1,参数2)
- 参数1:?的位置编号,从1开始
- 参数2:?的值
- 方法:setXxx(参数1,参数2)
-
执行sql,接受返回结果,不需要传递sql语句
-
处理结果
-
释放资源
-
-
-
注意:后期都会使用PreparedStatement对象来完成增删查改的所有操作
- 可以防止sql注入
- 效率更高
当我们使用PreparedStatement类时,代码如下:
package cn.itcast.jdbc;
import cn.itcast.util.JDBCUtils;
import java.sql.*;
import java.util.Scanner;
/**
* 通过键盘录入用户名和密码
* 判断是否登陆成功
*/
public class JDBCdemo10 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 1. 键盘录入,判断用户名和密码
String user = in.nextLine();
String password = in.nextLine();
// 2. 调用方法
boolean value = new JDBCdemo10().login2(user, password);
// 3. 判断结果
if(value){
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");
}
}
//使用Statement实现
public boolean login(String username, String password){
if(username == null || password == null){
return false;
}
Connection con = null;
Statement sta = null;
ResultSet re = null;
try {
// 1. 获取数据库连接对象
con = JDBCUtils.getConnection();
// 2. 定义sql语句
String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
System.out.println(sql);
// 3. 获取执行sql对象
sta = con.createStatement();
// 4. 执行sql并返回结果
re = sta.executeQuery(sql);
// 5. 处理结果
return re.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(re, sta, con);
}
return false;
}
//使用Preparement实现
public boolean login2(String username, String password){
if(username == null || password == null){
return false;
}
Connection con = null;
PreparedStatement pre = null;
ResultSet re = null;
try {
// 1. 获取数据库连接对象
con = JDBCUtils.getConnection();
// 2. 定义sql语句
String sql = "select * from user where username = ? and password = ? ";
// 3. 获取执行sql对象
pre = con.prepareStatement(sql);
pre.setString(1, username);
pre.setString(2, password);
System.out.println(sql);
// 4. 执行sql并返回结果
re = pre.executeQuery();
// 5. 处理结果
return re.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(re, pre, con);
}
return false;
}
}
当我们再次输入相同的用户名和密码时,发现如下结果:
我们发现登录失败,因此很好的解决了sql注入问题。
JDBC工具类:JDBCUtils
什么是JDBCUtils工具类呢?我们发现,每次在使用JDBC时,步骤基本如下:
- 注册驱动
- 获取数据库连接对象
- 定义sql语句
- 获取执行sql语句对象
- 执行sql并返回结果
- 处理结果
- 释放资源
我们从上面可以看出,我们在获取数据库连接对象时每次都要重新写一下url、user、root非常繁琐,而且每次在释放资源时都要写那么一段长长的代码,很繁琐。而且这两个操作一般都是很固定的,那么我们可不可以把这两个操作封装成工具类,这样我们每次调用的时候只要调用工具类就可以了,很简便。那么我们怎样写这个JDBCUtils工具类呢?
我们写一个配置文件,名字叫做:jdbc.properties,里面写上如下格式,url = 后面写连接数据库的位置,user=后写数据库的用户名,password=后写数据库的密码,driver厚些com.mysql.jdbc.Driver这其实是我们将Driver类加载进内存,这是注册驱动操作。而上面的url、user、password是在数据库连接时我们需要传递到连接方法的参数。
我们写的JDBCUti工具类如下:
在获取数据库连接对象时,我们只需更改一下配置文件中的参数,调用getConnection() 方法即可。
在释放资源时,我们只需将数据库连接对象,执行sql对象,如果有结果集对象的话,也把结果集对象作为参数传递进close方法,直接调用close方法就可以完成释放资源的操作了,很简便。
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
static {
try {
//读取资源文件,获取值
// 1. 创建Properties集合类
Properties pro = new Properties();
ClassLoader cls = JDBCUtils.class.getClassLoader();
URL res = cls.getResource("jdbc.properties");
String path = res.getPath();
System.out.println(path);
// 2. 加载文件
//pro.load(new FileReader("D:\\javaJDBC\\src\\jdbc.properties"));
pro.load(new FileReader(path));
// 3. 获取数据、赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
/**
*
* 释放连接
*/
public static void close(Statement sta, Connection con){
if(sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 释放连接
* @param res
* @param sta
* @param con
*/
public static void close(ResultSet res, Statement sta, Connection con){
if(res != null){
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
下面是一个使用JDBCUtils的代码:
/**
* 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回
* JDBCUtils工具类的演示
*/
public class JDBCdemo9 {
public static void main(String[] args) {
List<Emp> list = new JDBCdemo9().findAll();
System.out.println(list.size());
list.forEach(System.out::println);
}
public List<Emp> findAll(){
Connection con = null;
Statement sta = null;
ResultSet re = null;
List<Emp> list = new ArrayList<Emp>();
Emp emp = null;
try {
// 1. 注册驱动
// 2. 定义sql语句
String sql = "select * from emp";
// 3. 获取数据库连接对象
con = JDBCUtils.getConnection();
// 4. 获取sql执行对象
sta = con.createStatement();
// 5. 执行sql语句并返回结果
re = sta.executeQuery(sql);
while(re.next()){
int id = re.getInt("id");
String ename = re.getString("ename");
int job_id = re.getInt("job_id");
int mgr = re.getInt("mgr");
Date joindate = re.getDate("joindate");
double salary = re.getDouble("salary");
double bonus = re.getDouble("bonus");
int dept_id = re.getInt("dept_id");
emp = new Emp();
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
list.add(emp);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtils.close(re, sta, con);
}
return list;
}
}
我们可以发现相较于之前我们写过的代码来说,注册驱动、获取数据库连接对象、释放资源的操作非常简便,大大提高了我们写代码的速度。
JDBC控制事务
- 什么是事务呢?
一个包含多个步骤的业务操作,如果交由事务管理,那么这多个步骤要么同时成功,要么同时失败。
- 操作
- 开启事务
- 提交事务
- 回滚事务
- 使用Connection对象来管理事务
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
- 注意:在执行sql之前开启事务
- 提交事务:commit()
- 注意:当所有sql都执行完再提交事务
- 回滚事务:rollback()
- 在catch中回滚事务
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
下面是一个包含多个步骤的业务操作:张三借给李四500元钱
代码如下:
public class JDBCdemo11 {
public static void main(String[] args) {
Connection con = null;
PreparedStatement pre1 = null;
PreparedStatement pre2 = null;
try{
// 1. 注册驱动
con = JDBCUtils.getConnection();
con.setAutoCommit(false); //开启事务
// 2. 定义sql语句
// 2.1 张三减500
String sql1 = "update account set balance = balance - ? where id = ?";
String sql2 = "update account set balance = balance + ? where id = ?";
// 3. 定义sql执行对象
pre1 = con.prepareStatement(sql1);
pre2 = con.prepareStatement(sql2);
pre1.setDouble(1, 500);
pre1.setInt(2, 1);
pre2.setDouble(1, 500);
pre2.setInt(2, 2);
pre1.executeUpdate();
int i = 3/0;
pre2.executeUpdate();
con.commit(); //提交事务
} catch (Exception throwables) {
if(con != null){
try {
con.rollback(); //回滚
} catch (SQLException e) {
e.printStackTrace();
}
}
throwables.printStackTrace();
} finally {
JDBCUtils.close(pre1, con);
JDBCUtils.close(pre2, con);
}
}
}