1.什么叫数据库事务?
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
一组逻辑操作单元:一个或多个DML操作。
2.事务处理的原则:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下载:要么数据库管理系统将放弃所作的所有修改,整个事务回滚roolback到最初状态。
3.数据一旦提交,就不可回滚。
4.哪些操作会导致数据的自动提交?
DDL操作一旦执行,都会自动提交。set autocommit = false 对DDL操作失效。
DML默认情况下,一旦执行,就会自动提交。我们可以通过set autocommit = false的方式取消DML操作的自动提交。
默认在关闭连接时,会自动的提交数据
只有同时防止上面三种情况,才能防止数据的自动提交。
正常情况下,若不发生异常
/*
针对于数据表user_table来说:
AA用户给BB用户转账100
update user_table set balance = balance - 100 where user = 'AA';
update user_table set balance = balance + 100 where user = 'BB';
*/
@Test
public void testUpdate(){
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(sql1,"AA");
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(sql2,"BB");
System.out.println("转账成功");
}
//通用的增删改操作 -----version 1.0
public int update(String sql,Object ...args){//sql中占位符的个数与可变形参的个数相同
Connection conn = null;
PreparedStatement ps = null;
try{
//1.获取数据库的连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0;i < args.length;i++)
{
ps.setObject(i+1,args[i]);
}
//4.执行
// ps.execute();
return ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
//5.资源的关闭
JDBCUtils.closeResource(conn,ps);
System.out.println("执行完毕!");
}
return 0;
}
//输出
转账成功
WX20191217-135839@2x.png
模拟网络异常
@Test
public void testUpdate(){
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(sql1,"AA");
//模拟网络异常
System.out.println(10/0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(sql2,"BB");
System.out.println("转账成功");
}
//输出
执行完毕!
java.lang.ArithmeticException: / by zero
at com.cloud.transaction.TransactionTest.testUpdate(TransactionTest.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
WX20191217-140554@2x.png
考虑数据库事务后的转账操作
@Test
public void testUpdateWithTx(){
Connection conn = null;
try{
conn = JDBCUtils.getConnection();
System.out.println(conn.getAutoCommit());//true
//1.取消数据的自动提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn, sql1, "AA");
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn, sql2, "BB");
System.out.println("转账成功");
//2.提交数据
conn.commit();
}catch (Exception e){
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
}catch (Exception ex){
ex.printStackTrace();
}
}finally {
JDBCUtils.closeResource(conn,null);
}
}
/*******************考虑数据库事务后的转账操作***************/
//通用的增删改操作 -----version 2.0
public int update(Connection conn,String sql,Object ...args){//sql中占位符的个数与可变形参的个数相同
PreparedStatement ps = null;
try{
//1.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0;i < args.length;i++)
{
ps.setObject(i+1,args[i]);
}
//4.执行
return ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
//5.资源的关闭
JDBCUtils.closeResource(null,ps);
System.out.println("执行完毕!");
}
return 0;
}
//输出
true
执行完毕!
执行完毕!
转账成功
WX20191217-160510@2x.png
模拟网络异常情况
@Test
public void testUpdateWithTx(){
Connection conn = null;
try{
conn = JDBCUtils.getConnection();
//1.取消数据的自动提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn, sql1, "AA");
//模拟网络异常情况
System.out.println(10/0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn, sql2, "BB");
System.out.println("转账成功");
//2.提交数据
conn.commit();
}catch (Exception e){
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
}catch (Exception ex){
ex.printStackTrace();
}
}finally {
JDBCUtils.closeResource(conn,null);
}
}
//输出
java.lang.ArithmeticException: / by zero
WX20191217-161303@2x.png
Connection没有被关闭,还可能被重复使用,则需要回复其自动提交状态setAutoCommit(true)。尤其是在使用数据库连接池技术时,执行close()方法前,建议恢复自动提交状态。
//通用的增删改操作 -----version 2.0
public int update(Connection conn,String sql,Object ...args){//sql中占位符的个数与可变形参的个数相同
PreparedStatement ps = null;
try{
//1.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0;i < args.length;i++)
{
ps.setObject(i+1,args[i]);
}
//4.执行
return ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
//修改其为自动提交数据
//主要针对于使用数据库连接池的使用
//5.资源的关闭
JDBCUtils.closeResource(null,ps);
System.out.println("执行完毕!");
}
return 0;
}
网友评论