跟大家分享一下事务的简单应用。事务是什么,就像我们日常经常遇到的付款问题,商家扫我的二维码扣款,我的钱要被扣掉,如果我给了钱,商家没收到;或者商家显示我已经付款了,但是我的钱还在。这两种情况是不符合我们日常的实际的,事务就是为了解决这类问题,只要有一方执行失败,则必须都失败(回滚);只有双方都执行成功的时候才能提交。
这里我用添加员工信息来举例
在两张表同时新增同一个人的信息
数据库准备:BONUS,EMP(scott自带的表)
--------------------------Emp-----------------------------------
package entity;
/**
* 雇员实体类
* @author hp
*
*/
public class Emp {
private int empno;
private String ename;
private String job;
private int deptno;
public Emp() {
super();
}
public Emp(int empno, String ename, String job, int deptno) {
super();
this.empno = empno;
this.ename = ename;
this.job = job;
this.deptno = deptno;
}
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
return "Emp [empno=" + empno + ", ename=" + ename + ", job=" + job + ", deptno=" + deptno + "]";
}
}
-------------------------------------------------------------------
工具类:JDBCUtil
--------------------------JDBCUtil--------------------------------
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCUtil {static {
try {
Class.forName("oracle.jdbc.driver.OracleDriver"); //加载驱动
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection conn=null;
try {
//获得连接
String url="jdbc:oracle:thin:@localhost:1521:orcl";
String user = "scott"; //数据库账号
String password = "123456"; //数据库密码
} catch (SQLException e) {
e.printStackTrace();
}
return conn; //返回连接
}
public static void close(ResultSet re,Statement stat) { //关闭结果集和Statement(prepareStatement);
try {
if(re!=null) {
re.close(); //关闭结果集
}
if(stat!=null) {
stat.close(); //关闭Statement或者prepareStatement(传进来的是哪个类型就关闭哪个,prepareStatement是Statement的子类)
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(Connection conn) {//关闭连接
try {
if(conn!=null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void beginTransaction(Connection conn) { //开启事务
try {
conn.setAutoCommit(false); //防止自动提交,setAutoCommit()默认是true:自动提交
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void commitTransaction(Connection conn) {
try {
conn.commit(); //事务提交
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void rollBackTransaction(Connection conn) {
try {
conn.rollback(); //事务回滚
} catch (SQLException e) {
e.printStackTrace();
}
}
}
-------------------------------------------------------------------
Dao层:
准备类EmpDaoImpl,BonusDaoImpl
---------------------BonusDaoImpl------------------------------
package dao.impl;
import java.sql.Connection;import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import dao.IBonusDao;
import dao.IEmpDao;
import entity.Emp;
import util.JDBCUtil;
public class BonusDaoImpl implements IBonusDao{
PreparedStatement stat=null;
Connection conn=null;
ResultSet rs=null;
public BonusDaoImpl(Connection conn) {
this.conn=conn;
}
/**
* 新增雇员
* @throws SQLException
*/
public boolean insertBonus(Emp emp) {
try {
stat=conn.prepareStatement("insert into Bonus(ename,job) values(?,?)");
stat.setString(1, emp.getEname());
stat.setString(2, emp.getJob());
int result=stat.executeUpdate();
if(result>0) {
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtil.close(rs, stat);
}
return false;
}
}
-----------------------------------------------------------------------
----------------------------EmpDaoImpl-----------------------------
package dao.impl;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import dao.IEmpDao;
import entity.Emp;
import util.JDBCUtil;
public class EmpDaoImpl implements IEmpDao{
PreparedStatement stat=null;
Connection conn=null;
ResultSet rs=null;
public EmpDaoImpl(Connection conn) {
this.conn=conn;
}
/**
* 新增雇员
* @throws SQLException
*/
public boolean insertEmp(Emp emp) {
try {
String sql=insert into emp(empno,ename,job,deptno) values(TAB_USER_SEQ.NEXTVAL,?,?,?)
stat=conn.prepareStatement(sql);
stat.setString(1, emp.getEname());
stat.setString(2, emp.getJob());
stat.setInt(3, emp.getDeptno());
int result=stat.executeUpdate();
if(result>0) {
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtil.close(rs, stat);
}
return false;
}
}
service层:
--------------------EmpService---------------------------------
package service;
import java.sql.Connection;
import dao.IBonusDao;
import dao.IEmpDao;
import dao.impl.BonusDaoImpl;
import dao.impl.EmpDaoImpl;
import entity.Emp;
import util.JDBCUtil;
public class EmpService {
Connection conn=null;
public boolean addEmp(Emp emp) {
try {
conn=JDBCUtil.getConnection(); //获取数据库连,只在这里用连接,保证两张表用的是同一个连接JDBCUtil.beginTransaction(conn); //开启事务,防止自动提交
/*新增的员工信息*/IEmpDao dao=new EmpDaoImpl(conn); //连接通过构造方法传进给Emp表
boolean result =dao.insertEmp(emp);
//异常,执行失败
int i=1/0; //这里不是是指两张表之间可能会有其他事情在操作,这里只是简单代替
//新增奖金表信息----------------------------执行失败----------------------
IBonusDao bonusDao = new BonusDaoImpl(conn);连接通过构造方法传进给Bonus表
boolean bonusResult = bonusDao.insertBonus(emp);
if(result && bonusResult) { //判断两表是否都增加了信息
JDBCUtil.commitTransaction(conn); //提交}else {
JDBCUtil.rollBackTransaction(conn); //回滚
}
JDBCUtil.close(conn); //关闭资源,这里只是关掉连接
return result && bonusResult;
} catch (Exception e) {
e.printStackTrace();
JDBCUtil.rollBackTransaction(conn);//整段程序有异常时事务回滚(没有添加数据)
}
return false;
}
}
-------------------------------------------------------------------------
-------------------------------main方法的代码--------------------------
Emp emp=new Emp();
emp.setEname(ename); //这里的ename,job,deptno可以试着输入不同的数据
emp.setJob(job);
emp.setDeptno(Integer.valueOf(deptno));
EmpService service=new EmpService();
boolean result =service.addEmp(emp);
System.out.println("新增结果:"+result);
---------------------------------------------------------------------------
这里只提供了一个Emp员工类,因为两张表所要添加的是同一个人的信息
EmpDaoImpl和BonusDaoImpl中使用构造方法来获取数EmpService中的据库连接是为了确保在操作数据库是用的是同一个连接,保证数据的一致(可以再PL/SQL打开两个sql的页面,对一张先后进行增加或者其他操作数据的操作,会发现两张数据库的数据时不一样的)
service层的EmpService类里面,在新增的两张表的信息中间包含其他执行语句,这是为了更直观的反映这两张表在新增数据时如果中间遇到一些异常或者错误的情况,整件事务都必须回滚;如果中间的执行语句能成功执行,但是两张表有一张不能添加数据也必须回滚(代码中用if语句进行的判断),只有当语句没有问题是才能顺利的同时提交到两张表中。