JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇)
Java学习日志(三十五)
事务
事务概述
事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败。
事务作用:保证在一个事务(一个connection)中多次SQL操作要么全都成功,要么全都失败。 防止出现转账吞钱的现象。
数据库有事务概念,执行sql语句,就会开启事务,执行成功会提交事务,执行失败会回滚事务:
- mysql数据库事务默认都是自动的,自动开启事务,自动提交事务,自动回滚事务
- oracle数据库事务默认都是手动的,手动开启事务,手动提交事务,手动回滚事务
注意:事务一旦结束(提交,回滚),数据就永久保存在数据库
在Connection接口中,有操作事务的方法:
-
void setAutoCommit(boolean autoCommit)
将此连接的自动提交模式设置为给定状态。参数: autoCommit:true启用自动提交模式(默认); false禁用自动提交模式,(执行sql语句之前)手动开启事务。
-
void commit()
一组sql一句都执行成功,提交事务 -
void rollback()
一组sql中有一条执行失败,回滚事务;把数据回滚到数据开启之前
使用原生JDBC完成转账案例
首先,创建一张账户表
# 创建一个表:账户表.
CREATE DATABASE day05;
# 使用数据库
USE day05;
# 创建账号表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
money DOUBLE
);
# 初始化数据
INSERT INTO account VALUES (NULL,'jack',10000);
INSERT INTO account VALUES (NULL,'rose',10000);
INSERT INTO account VALUES (NULL,'tom',10000);
代码示例:使用原生JDBC完成转账案例
public class Demo01JDBC {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取数据库连接对象Connection
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day05", "root", "root");
//开启事务
conn.setAutoCommit(false);
//获取执行者对象Statement
stat = conn.createStatement();
//执行sql语句获取结果
int row1 = stat.executeUpdate("UPDATE account SET money=money-1000 where NAME='jack';");
System.out.println(0/0);
int row2 = stat.executeUpdate("UPDATE account SET money=money+1000 where NAME='rose';");
//处理结果
if (row1 > 0 && row2 > 0) {
System.out.println("转账成功");
//提交事务
conn.commit();
}
} catch (Exception e) {
System.out.println("转账失败");
e.printStackTrace();
/*
回滚事务
*/
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}finally {
//释放资源
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
使用DbUtils和C3P0完成转账案例
QueryRunner的构造方法:
QueryRunner()
:空参构造,调用update/query方法执行sql语句时,必须传递Connection对象。QueryRunner(DataSource ds)
:带连接池的构造方法,看不到Connection,也就不能操作事务。
QueryRunner的成员方法:用于执行增删改的update方法。
int update(Connection conn,String sql,Object... params)
空参构造使用。
代码示例:使用DBUtils+C3P0连接池完成转账案例
public class Demo02DBUtils {
public static void main(String[] args) {
//1.创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//2.使用C3P0连接池获取Connection
Connection conn = C3P0UtilsXML.getConnection();
//3.使用QueryRunner对象中的方法update
String sql1 = "UPDATE account SET money=money-1000 where NAME='jack';";
String sql2 = "UPDATE account SET money=money+1000 where NAME='rose';";
try {
//开启事务
conn.setAutoCommit(false);
int row1 = qr.update(conn, sql1);
int row2 = qr.update(conn, sql2);
//4.处理结果
if (row1 > 0 && row2 > 0) {
System.out.println("转账成功");
//提交事务
conn.commit();
}
} catch (Exception e) {
System.out.println("转账失败");
e.printStackTrace();
//回滚事务
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
//释放资源
DbUtils.closeQuietly(conn);
}
}
}
三层思想
三层思想概述
三层思想转账案例_dao层
创建转账案例的Dao层:用于对account表进行增删改查
注意:
一张表–>一个dao
定义两个方法:
一个减钱,一个加钱
代码示例:dao层
public class AccountDao {
/*
定义减钱方法
参数:
Connection conn:两个方法使用同一个Connection,从而保证使用同一个事务
String fromName:付款人姓名
double money:转账金额
返回值:
int:影响数据库的有效行数
String sql1 = "UPDATE account SET money=money-1000 where NAME='jack';";
String sql2 = "UPDATE account SET money=money+1000 where NAME='rose';";
*/
public int fromAccount(Connection conn, String fromName, double money) throws SQLException {
//创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//调用update方法执行sql语句,接收结果
int row = qr.update(conn,"UPDATE account SET money=money-? where NAME=?;",money,fromName);
//返回方法
return row;
}
/*
定义加钱方法
参数:
Connection conn:两个方法使用同一个Connection,从而保证使用同一个事务
String toName:收款人姓名
double money:转账金额
返回值:
int:影响数据库的有效行数
String sql1 = "UPDATE account SET money=money-1000 where NAME='jack';";
String sql2 = "UPDATE account SET money=money+1000 where NAME='rose';";
*/
public int toAccount(Connection conn, String toName, double money) throws SQLException {
//创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//调用update方法执行sql语句,接收结果
int row = qr.update(conn,"UPDATE account SET money=money+? where NAME=?;",money,toName);
//返回方法
return row;
}
}
三层思想转账案例_service层
转账案例的Service层:接收web层传递的数据,调用dao层的方法,接收结果;把结果返回给web层
定义一个转账方法:
- 参数接收web传递的数据(付款人姓名,收款人姓名,转账金额)
- 使用C3P0连接池,获取Connection
- 开启事务
- 创建AccountDao对象,调用减钱价钱方法,接收结果
- 对结果进行判断
- 执行成功,提交事务;有异常回滚事务
- 把结果返回给web层
- 释放资源
代码示例:service层
public class AccountService {
//定义一个转账方法
public boolean transferAccount(String fromName, String toName, double money) {
//使用C3P0连接池,获取Connection
Connection conn = C3P0UtilsXML.getConnection();
//定义返回的结果
boolean flag = false;
try {
//开启事务
conn.setAutoCommit(false);
//创建AccountDao对象
AccountDao dao = new AccountDao();
//调用减钱价钱方法,接收结果
int row1 = dao.fromAccount(conn, fromName, money);
int row2 = dao.toAccount(conn, toName, money);
//对结果进行判断
if (row1>0&&row2>0){
flag = true;
//执行成功,提交事务
conn.commit();
}
} catch (Exception e) {
e.printStackTrace();
//有异常回滚事务
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}finally {
//资源释放
DbUtils.closeQuietly(conn);
}
//把结果返回给web层
return flag;
}
}
三层思想转账案例_web层
创建转账案例的web层
- 使用Scanner获取用户输入的数据(付款人姓名,收款人姓名,转账金额)
- 创建AccountService对象
- 调用转账方法,接收转账结果
- 对结果进行判断,给用户展示结果
代码示例:web层
public class AccountWeb {
public static void main(String[] args) {
//使用Scanner获取用户输入的数据(付款人姓名,收款人姓名,转账金额)
Scanner sc = new Scanner(System.in);
System.out.print("请输入付款人姓名:");
String fromName = sc.next();
System.out.print("请输入收款人姓名:");
String toName = sc.next();
System.out.print("请输入转账金额:");
double money = sc.nextDouble();
//创建AccountService对象
AccountService service = new AccountService();
//调用转账方法,接收转账结果
boolean b = service.transferAccount(fromName, toName, money);
//对结果进行判断,给用户展示结果
if (b) {
System.out.println("转账成功");
} else {
System.out.println("转账失败");
}
}
}
转账结果: