溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

事務(wù)的提交與回滾

發(fā)布時(shí)間:2020-07-05 15:45:55 來源:網(wǎng)絡(luò) 閱讀:313 作者:淺嫣 欄目:開發(fā)技術(shù)


事務(wù)的提交與回滾

一.事務(wù)的概念

       事務(wù)就是某一組操作,要么都執(zhí)行,要么都不執(zhí)行。

       比如你要去銀行給朋友轉(zhuǎn)錢,必然存在著從你的賬戶里扣除一定的金額和向你朋友賬戶里增加相等的金額這兩個(gè)操作,這兩個(gè)操作是不可分割的,無論是哪一個(gè)操作的失敗,成功的操作也要恢復(fù)至最初的狀態(tài)才能使銀行和用戶雙方都滿意,而這樣的行為就牽扯到了事務(wù)的回滾

事務(wù)的特性:
       原子性:一個(gè)事務(wù)是不可分割的最小單位。
       一致性:一個(gè)事務(wù)在執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)(比如說轉(zhuǎn)賬,前后兩個(gè)賬戶的總金額是不會(huì)改變的)。
       隔離性:多個(gè)并發(fā)事務(wù)之間的操作不會(huì)互相干擾。

       持久性:提交的事務(wù)會(huì)使得修改的數(shù)據(jù)是永久的。

二.事務(wù)的實(shí)例分析

例子:向員工表中添加一個(gè)員工的記錄。

1.首先需要一個(gè)員工實(shí)體:

public class Emp {	private int empId;	private String ename;	private String sex ;	private Date birthday;	public int getEmpId() {		return empId;	}	public void setEmpId(int empId) {		this.empId = empId;	}	public String getEname() {		return ename;	}	public void setEname(String ename) {		this.ename = ename;	}	public String getSex() {		return sex;	}	public void setSex(String sex) {		this.sex = sex;	}	public Date getBirthday() {		return birthday;	}	public void setBirthday(Date birthday) {		this.birthday = birthday;	}	public Emp(int empId, String ename, String sex, Date birthday) {		super();		this.empId = empId;		this.ename = ename;		this.sex = sex;		this.birthday = birthday;	}	public Emp() {		super();	}	@Override	public String toString() {		return "Emp [empId=" + empId + ", ename=" + ename + ", sex=" + sex + ", birthday=" + birthday + "]";	}	public Emp(String ename, String sex, Date birthday) {		super();		this.ename = ename;		this.sex = sex;		this.birthday = birthday;	}}

2.還需要封裝一些數(shù)據(jù)庫的連接:

public class JDBCUtil {	Connection conn;	Statement stm;	ResultSet rs;	private static JDBCUtil instance;	private static Properties pro=new Properties();		static {				try {			InputStream in=JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");			pro.load(in);		} catch (IOException e) {			// TODO Auto-generated catch block			throw new RuntimeException("讀取配置文件失敗!");		}		}	private JDBCUtil() {}	public static JDBCUtil getInstance() {		if(instance==null) {			synchronized (JDBCUtil.class) {				if(instance==null) {					instance=new JDBCUtil();				}			}		}		return instance;	}	public Connection getConnection() throws ClassNotFoundException, SQLException {		Class.forName(pro.getProperty("driver"));		Connection  conn = DriverManager.getConnection(pro.getProperty("url"),pro.getProperty("username"),pro.getProperty("password"));				return conn;	}	public void release(ResultSet rs,Statement st,Connection conn) {		if(rs != null) {			try {				rs.close();			} catch (SQLException e) {				// TODO Auto-generated catch block				e.printStackTrace();			}finally {				if(st != null) {					try {						st.close();					} catch (SQLException e) {						// TODO Auto-generated catch block						e.printStackTrace();					}finally {						if(conn != null) {							try {								conn.close();							} catch (SQLException e) {								// TODO Auto-generated catch block								e.printStackTrace();							}						}					}				}			}		}	}}

3.配置文件:(在使用時(shí)注意更改為自己的數(shù)據(jù)庫)

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8username=root
password=root

4.添加員工:

public class TestTransacion {	Connection conn ;	Connection conn1 ;	Connection conn2 ;	PreparedStatement pstm;	PreparedStatement pstm1;	ResultSet rs;	public void addEmp(Emp emp) {		try {			conn = JDBCUtil.getInstance().getConnection();			conn.setAutoCommit(false);//設(shè)置事務(wù)非自動(dòng)提交			String sql = "insert into emp (ename,sex,birthday) values (?,?,?)";			pstm = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); //在sql未執(zhí)行之前,進(jìn)行預(yù)編譯			pstm.setString(1, emp.getEname());			pstm.setString(2, emp.getSex());			pstm.setDate(3, new java.sql.Date(emp.getBirthday().getTime()));			pstm.executeUpdate();			int id = 0;			rs = pstm.getGeneratedKeys();//獲取由于執(zhí)行此 Statement 對象而創(chuàng)建的所有自動(dòng)生成的鍵			if(rs.next()) {				id = rs.getInt(1);			}			System.out.println(id);			conn1 = JDBCUtil.getInstance().getConnection();			sql = "select * from emp where empid = "+id;			pstm1 = conn1.prepareStatement(sql);			rs = pstm1.executeQuery();			if(rs.next()) {				System.out.println(rs.getString("ename") +"11");			}			conn.commit();			conn2 = JDBCUtil.getInstance().getConnection();			sql = "select * from emp where empid = "+id;			pstm1 = conn2.prepareStatement(sql);						rs = pstm1.executeQuery();			if(rs.next()) {				System.out.println(rs.getString("ename")+"22");			}					} catch (ClassNotFoundException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (SQLException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}finally {			JDBCUtil.getInstance().release(rs, pstm, conn);			if(pstm1 != null) {				try {					pstm1.close();					conn1.close();					conn2.close();				} catch (SQLException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}	}	public static void main(String[] args) {		TestTransacion ttst=new TestTransacion();		Emp emp=new Emp("lucy", "女");		ttst.addEmp(emp);	}}

在添加員工時(shí)共聲明了三個(gè)Connection變量,在插入之前先設(shè)置事務(wù)為非自動(dòng)提交,之后用到了Statement的方法getGenerateKeys()來獲取剛剛插入的數(shù)據(jù)所自動(dòng)生成的鍵值(即empId值)可用于下面的查找,下面共有兩個(gè)查找語句,中間我們用commit提交了事務(wù),一個(gè)是提交前的查找,一個(gè)是提交后的查找,當(dāng)執(zhí)行完時(shí),我們會(huì)發(fā)現(xiàn)結(jié)果是lucy22,為什么呢?因?yàn)樘峤恢皵?shù)據(jù)庫里面還沒有東西肯定是查不到的。當(dāng)然切記需要用到不同的coon,因?yàn)槿绻檎視r(shí)用的coon和插入時(shí)的一樣的話,事務(wù)沒有提交也是可以查到的,因?yàn)橛玫降膶ο笫峭粋€(gè),里面已經(jīng)有數(shù)據(jù)了?。。?!

三.事務(wù)的回滾

       回滾:假設(shè)A給B轉(zhuǎn)賬,A的money--,B的money++,兩者是一個(gè)整體。只有兩個(gè)操作都成功完成了,事務(wù)才會(huì)提交,否則都會(huì)回滾到原來的狀態(tài)。

       在emp表里再添加一個(gè)salary屬性,把員工編號為1的員工的工資更改為1000,并且判斷員工編號為3的員工的工資是否大于5000,如果小于5000則加1000,這兩個(gè)操作為一個(gè)事務(wù),要想事務(wù)能夠正常的提交的條件是員工編號為3的員工的工資小于5000,工資加了1000,此時(shí)員工編號為1的員工的工資更改為1000.如果員工編號為3的員工的工資大于5000則中斷程序,拋出異常,即操作2沒有順利完成,事務(wù)不應(yīng)提交,而應(yīng)該回滾到最初的狀態(tài)。

       當(dāng)然,不是所有的時(shí)候都需要恢復(fù)最初的狀態(tài)的,所以有時(shí)候就會(huì)用到自己設(shè)置回滾點(diǎn),上述程序中注釋的部分就是自己添加了如果事務(wù)沒有正常提交想要恢復(fù)到程序執(zhí)行到哪個(gè)位置的回滾點(diǎn)。

public class TestTx {	public static void main(String[] args) throws SQLException {		Connection conn = null;		PreparedStatement  pstm = null;		ResultSet rs = null;		//Savepoint  sp = null;			try {			conn = JDBCUtil.getInstance().getConnection();			conn.setAutoCommit(false);			String sql = "update emp set salary = 1000 where empid = 1";			pstm = conn.prepareStatement(sql);			pstm.executeUpdate();						//sp = conn.setSavepoint(); //設(shè)置回滾點(diǎn)					sql = "select salary from emp where empid = 4";			pstm = conn.prepareStatement(sql);			rs = pstm.executeQuery();			if(rs.next()) {				double salary = rs.getDouble(1);				if(salary >5000) {					System.out.println("薪資大于5000");					throw new RuntimeException("薪資大于5000");				}			}			sql = "update emp set salary = salary + 1000 where empid = 4";			pstm = conn.prepareStatement(sql);			pstm.executeUpdate();			conn.commit();					} catch(RuntimeException e) {			/*if(conn != null && sp != null) {				conn.rollback(sp);			}			conn.commit();			throw e ;*/			e.printStackTrace();		}catch (ClassNotFoundException e) {			e.printStackTrace();		} catch (SQLException e) {			if(conn != null) {				conn.rollback();			}			throw e;		}finally {			JDBCUtil.getInstance().release(rs, pstm, conn);		}	}}



向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI