事务:
在编写业务的过程中,会需要进行事务处理,当需要执行多条插入语句时,如果前几条成功,而最后一条失败,那么我们需要回滚数据库操作,保持数据的一致性和完整性,此时,就需要利用DB的事务处理。事务是恢复和并发控制的基本单位。
简单来说,所谓的事务,是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
1.示例
当person1给person2转账10块:
数据库实现思路如下:先给person1减去10,再给2加上10。
那么问题来了:
当person1减去10执行成功以后,在给person2加钱的时候出错了。
最终造成结果是:
person1少了10快,person2的钱却没有变。因此,总钱少了10快。
2.没有事务,正常执行
package tran;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
//实现的业务是:1给2转10块钱
public class Account {
private static String jdbcDriver = "com.mysql.jdbc.Driver";// mysql连接驱动,无需改
private static String jdbcUrl = "jdbc:mysql://localhost:3306/zdy";
private static String jdbcuser = "root"; // 数据库用户名
private static String jdbcpwd = "root"; // 数据库密码
private static Connection conn;
public static PreparedStatement ps;
public static void main(String[] args) {
try {
Class.forName(jdbcDriver);
conn = DriverManager.getConnection(jdbcUrl, jdbcuser, jdbcpwd); // 驱动利用驱动地址,数据库用户名,密码创建连接
ps = conn.prepareStatement("UPDATE account set money=money-10 where uid=person1;");
ps.executeUpdate();
ps = conn.prepareStatement("UPDATE account set money=money+10 where uid=person2;");
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
}
}
}
//最终的执行结果是:从id person1的用户里减去了10块钱,但是并没有给person2加上。
//造成的结果是:总金额少了10块。
package tran;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class Account {
private static String jdbcDriver = "com.mysql.jdbc.Driver";// mysql连接驱动,无需改
private static String jdbcUrl = "jdbc:mysql://localhost:3306/zdy";
private static String jdbcuser = "root"; // 数据库用户名
private static String jdbcpwd = "root"; // 数据库密码
private static Connection conn;
public static PreparedStatement ps;
public static void main(String[] args) {
try {
Class.forName(jdbcDriver);
conn = DriverManager.getConnection(jdbcUrl, jdbcuser, jdbcpwd); // 驱动利用驱动地址,数据库用户名,密码创建连接
ps = conn.prepareStatement("UPDATE account set money=money-10 where uid=person1;");
ps.executeUpdate();
int i=1/0;//这里设置一个错误。
ps = conn.prepareStatement("UPDATE account set money=money+10 where uid=person2;");
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
}
}
}
4.解决方案-事务
package tran;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Account {
private static String jdbcDriver = "com.mysql.jdbc.Driver";// mysql连接驱动,无需改
private static String jdbcUrl = "jdbc:mysql://localhost:3306/zdy";
private static String jdbcuser = "root"; // 数据库用户名
private static String jdbcpwd = "root"; // 数据库密码
private static Connection conn;
public static PreparedStatement ps;
public static void main(String[] args) {
try {
Class.forName(jdbcDriver);
conn = DriverManager.getConnection(jdbcUrl, jdbcuser, jdbcpwd); // 驱动利用驱动地址,数据库用户名,密码创建连接
conn.setAutoCommit(false);// 设置不自动提交
ps = conn.prepareStatement("UPDATE account set money=money-10 where uid=person1;");
ps.executeUpdate();
int i = 1 / 0;
ps = conn.prepareStatement("UPDATE account set money=money+10 where uid=person2;");
ps.executeUpdate();
conn.commit();// 如果上面的执行都没问题,再提交。
} catch (Exception e) {
try {
conn.rollback(); //如果出错则 执行回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
// 关闭资源
}
}
}
回滚的作用就是,当有一个SQL语句执行时,条件不符合要求,比如你要插入一个数据,但是插入的数据要有条件的,这时候你就可以用回滚,如果条件成功就COMMIT提交的意思,不然就ROLLBACK回滚,也就是说插入不成功
使用场景:
person1给person2转钱,转完给他们发短信。
以下是模拟。
package tran;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;
public class Account {
private static String jdbcDriver = "com.mysql.jdbc.Driver";// mysql连接驱动,无需改
private static String jdbcUrl = "jdbc:mysql://localhost:3306/zdy";
private static String jdbcuser = "root"; // 数据库用户名
private static String jdbcpwd = "root"; // 数据库密码
private static Connection conn;
public static PreparedStatement ps;
static Savepoint sp;
public static void main(String[] args) {
try {
Class.forName(jdbcDriver);
conn = DriverManager.getConnection(jdbcUrl, jdbcuser, jdbcpwd); // 驱动利用驱动地址,数据库用户名,密码创建连接
conn.setAutoCommit(false);// 设置不自动提交
ps = conn.prepareStatement("UPDATE account set money=money-10 where uid=person1;");
ps.executeUpdate();
ps = conn.prepareStatement("UPDATE account set money=money+10 where uid=person2;");
ps.executeUpdate();
sp = conn.setSavepoint();//设置一个保存点
// 发送短信业务
// 模拟业务出错
int i = 1 / 0;//因为不是重要业务部分,所以出错后回滚到保存点提交数据到数据库
conn.commit();// 如果上面的执行都没问题,再提交。
} catch (Exception e) {
if(sp!=null){//只简单说明,实际还要判断conn是不是null
try {
conn.rollback(sp);//回滚到内个设置点
conn.commit();//提交保存点之前的数据到数据库
} catch (SQLException e1) {
e1.printStackTrace();
}elseif(sp==null){ conn.rollback();//若果保存点之前出错出错 则回滚全部
}
}
e.printStackTrace();
} finally {
// 关闭资源
}
}
}