1、 事务概述
事务的概念:逻辑上的一组SQL语句(一个或者多个)执行的时候要么全部执行成功,要么全部执行失败。
事务的案例:转账案例:一个账户中的余额减少,另一个账户中的余额增加。
更新数据:update set 余额=原有的余数 - 要转账的余额 where 转出账户的账户
更新语句:update set 余额=原有的余额+ 要转账的余额 where 转入账户的账户
要执行的SQL语句是两个update语句,要么都成功,要么都失败,不允许一个执行成功,另一个执行失败。
同一组SQL语句,就是用同一个数据库连接对象执行的!
-
数据准备
-- 1:创建一个银行数据库
create database if not exists bank;
-- 2: 打开数据库
use bank;
-- 3: 创建银行账户
create table if not exists account(
id int primary key auto_increment,
name varchar(20),
money double
);
-- 4:插入tom和jerry的账户表1000
insert into account(name, money) VALUES ('tom',2000),('jerry',2000);
-- 5:查询数据表
select * from account;
-- 6:修改数据表
update account set money=2000;
2、 事务的具体操作
开启事务,或者称为阻止事务的自动提交
提交事务,所有SQL语句都执行成功,提交事务,数据持久化
回滚事务,如果有部分SQL语句执行失败,回滚事务,数据不会改变
数据库自身事务的操作命令:
命令 | 命令的作用 |
---|---|
begin | 开启事务 |
commit | 提交事务 |
rollback | 回滚事务 |
Java中的事务操作:核心是数据库连接对象Connection:
数据库连接对象的方法 | 方法的作用 |
---|---|
setAutoCommit(false) | 开启事务,阻止事务的自动提交 |
commit | 提交事务 |
rollback | 回滚事务 |
每个数据库连接对象都有自己的事务,针对当前的数据库连接对象是有效的!
一旦事务提交或者回滚,当前事务就结束了!
7.3 原始JDBC转账案例中的事务
需求:JDBC技术实现转账功能,tom账户向jerry账户转1000元。
①:添加Lib库中的jar包
②:druid.properties配置文件
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/school
username=root
password=mysql密码
initialSize=10
maxActive=20
③:创建units包指的是DataSourceUtils
package com.rocky.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DataSourceUtils {
static DataSource dataSource;
static{
InputStream resourceAsStream =
DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties=new Properties();
try {
properties.load(resourceAsStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static DataSource getDataSource(){
return dataSource;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void close(ResultSet resultSet, Statement statement,Connection connection) throws SQLException {
if(resultSet!=null){
resultSet.close();
}
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();
}
}
}
④:准备pojo中的javaBean
package com.rocky.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private Integer id;
private String name;
private double money;
}
⑤:不使用事务操作
创建一个NoTransfer的事务类
package com.rocky.transfer;
import com.rocky.utils.DataSourceUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 实现转账功能
* 功能的实现步骤
* 1:定义需要的数据,收款人和付款人的姓名,转账金额
* 2: 定义2个更新语句 update语句
* 3:使用同一个数据库连接对象执行SQL语句
* */
public class NoTransfer {
public static void main(String[] args) throws SQLException {
// 付款人的姓名tom
String fromName="tom";
// 收款人的姓名jerry
String toName="jerry";
//转账金额
double money=500;
//定义数据库操作需要的对象
Connection connection=null;
PreparedStatement preparedStatement=null;
try {
//从连接池中获取数据库连接
connection= DataSourceUtils.getConnection();
//获取SQL语句的执行对象
// 减钱的SQL语句
String sql1="update account set money=money-? where name=?";
preparedStatement=connection.prepareStatement(sql1);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,fromName);
//执行SQL语句
int num1 = preparedStatement.executeUpdate();
// // 增加钱的SQL语句
String sql2="update account set money=money+? where name=?";
preparedStatement=connection.prepareStatement(sql2);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,toName);
//执行SQL语句
int num2=preparedStatement.executeUpdate();
//打印出结果
System.out.println("num1="+num1+"num2="+num2);
}catch (Exception e){
e.printStackTrace();
}finally {
DataSourceUtils.close(null,preparedStatement,connection);
}
}
}
执行成功:
中间人为的加一个错误
//中间加一句话 模拟出现一个错误 double a=1/0;
package com.rocky.transfer;
import com.rocky.utils.DataSourceUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 实现转账功能
* 功能的实现步骤
* 1:定义需要的数据,收款人和付款人的姓名,转账金额
* 2: 定义2个更新语句 update语句
* 3:使用同一个数据库连接对象执行SQL语句
* */
public class NoTransfer {
public static void main(String[] args) throws SQLException {
// 付款人的姓名tom
String fromName="tom";
// 收款人的姓名jerry
String toName="jerry";
//转账金额
double money=500;
//定义数据库操作需要的对象
Connection connection=null;
PreparedStatement preparedStatement=null;
try {
//从连接池中获取数据库连接
connection= DataSourceUtils.getConnection();
//获取SQL语句的执行对象
// 减钱的SQL语句
String sql1="update account set money=money-? where name=?";
preparedStatement=connection.prepareStatement(sql1);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,fromName);
//执行SQL语句
int num1 = preparedStatement.executeUpdate();
//中间加一句话
double a=1/0;
// // 增加钱的SQL语句
String sql2="update account set money=money+? where name=?";
preparedStatement=connection.prepareStatement(sql2);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,toName);
//执行SQL语句
int num2=preparedStatement.executeUpdate();
//打印出结果
System.out.println("num1="+num1+"num2="+num2);
}catch (Exception e){
e.printStackTrace();
}finally {
DataSourceUtils.close(null,preparedStatement,connection);
}
}
}
这就是没有事务操作的写法
⑥:使用事务的操作
package com.rocky.transfer;
import com.rocky.utils.DataSourceUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 实现转账功能
* 功能的实现步骤
* 1:定义需要的数据,收款人和付款人的姓名,转账金额
* 2: 定义2个更新语句 update语句
* 3:使用同一个数据库连接对象执行SQL语句
* */
public class NoTransfer {
public static void main(String[] args) throws SQLException {
// 付款人的姓名tom
String fromName="tom";
// 收款人的姓名jerry
String toName="jerry";
//转账金额
double money=500;
//定义数据库操作需要的对象
Connection connection=null;
PreparedStatement preparedStatement=null;
try {
//从连接池中获取数据库连接
connection= DataSourceUtils.getConnection();
// 关闭事务的自动提交
connection.setAutoCommit(false);
//获取SQL语句的执行对象
// 减钱的SQL语句
String sql1="update account set money=money-? where name=?";
preparedStatement=connection.prepareStatement(sql1);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,fromName);
//执行SQL语句
int num1 = preparedStatement.executeUpdate();
//中间加一句话
double a=1/0;
// // 增加钱的SQL语句
String sql2="update account set money=money+? where name=?";
preparedStatement=connection.prepareStatement(sql2);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,toName);
//执行SQL语句
int num2=preparedStatement.executeUpdate();
//打印出结果
System.out.println("num1="+num1+"num2="+num2);
//SQL语句执行完毕,没有发生异常,说明执行成功了,提交事务处理
connection.commit();
}catch (Exception e){
e.printStackTrace();
//如果出现异常,要回滚事务,注意 要用Exception去抓取,SQLException抓不到
connection.rollback();
}finally {
DataSourceUtils.close(null,preparedStatement,connection);
}
}
}
执行成功:
package com.rocky.transfer;
import com.rocky.utils.DataSourceUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 实现转账功能
* 功能的实现步骤
* 1:定义需要的数据,收款人和付款人的姓名,转账金额
* 2: 定义2个更新语句 update语句
* 3:使用同一个数据库连接对象执行SQL语句
* */
public class NoTransfer {
public static void main(String[] args) throws SQLException {
// 付款人的姓名tom
String fromName="tom";
// 收款人的姓名jerry
String toName="jerry";
//转账金额
double money=500;
//定义数据库操作需要的对象
Connection connection=null;
PreparedStatement preparedStatement=null;
try {
//从连接池中获取数据库连接
connection= DataSourceUtils.getConnection();
// 关闭事务的自动提交
connection.setAutoCommit(false);
//获取SQL语句的执行对象
// 减钱的SQL语句
String sql1="update account set money=money-? where name=?";
preparedStatement=connection.prepareStatement(sql1);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,fromName);
//执行SQL语句
int num1 = preparedStatement.executeUpdate();
//中间加一句话
// double a=1/0;
// // 增加钱的SQL语句
String sql2="update account set money=money+? where name=?";
preparedStatement=connection.prepareStatement(sql2);
preparedStatement.setObject(1,money);
preparedStatement.setObject(2,toName);
//执行SQL语句
int num2=preparedStatement.executeUpdate();
//打印出结果
System.out.println("num1="+num1+"num2="+num2);
//SQL语句执行完毕,没有发生异常,说明执行成功了,提交事务处理
connection.commit();
}catch (Exception e){
e.printStackTrace();
//如果出现异常,要回滚事务,注意 要用Exception去抓取,SQLException抓不到
connection.rollback();
}finally {
DataSourceUtils.close(null,preparedStatement,connection);
}
}
}
一次要执行多条SQL语句(增删改)的时候,会出现数据错误问题,因此必须要添加事务!