事務就是某一組操作,要么都執行,要么都不執行。
比如你要去銀行給朋友轉錢,必然存在著從你的賬戶里扣除一定的金額和向你朋友賬戶里增加相等的金額這兩個操作,這兩個操作是不可分割的,無論是哪一個操作的失敗,成功的操作也要恢復至最初的狀態才能使銀行和用戶雙方都滿意,而這樣的行為就牽扯到了事務的回滾。
事務的特性:
原子性:一個事務是不可分割的最小單位。
一致性:一個事務在執行之前和執行之后都必須處于一致性狀態(比如說轉賬,前后兩個賬戶的總金額是不會改變的)。
隔離性:多個并發事務之間的操作不會互相干擾。
持久性:提交的事務會使得修改的數據是永久的。
例子:向員工表中添加一個員工的記錄。
1.首先需要一個員工實體:
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.還需要封裝一些數據庫的連接:
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.配置文件:(在使用時注意更改為自己的數據庫)
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);//設置事務非自動提交 String sql = "insert into emp (ename,sex,birthday) values (?,?,?)"; pstm = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); //在sql未執行之前,進行預編譯 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();//獲取由于執行此 Statement 對象而創建的所有自動生成的鍵 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); }}
在添加員工時共聲明了三個Connection變量,在插入之前先設置事務為非自動提交,之后用到了Statement的方法getGenerateKeys()來獲取剛剛插入的數據所自動生成的鍵值(即empId值)可用于下面的查找,下面共有兩個查找語句,中間我們用commit提交了事務,一個是提交前的查找,一個是提交后的查找,當執行完時,我們會發現結果是lucy22,為什么呢?因為提交之前數據庫里面還沒有東西肯定是查不到的。當然切記需要用到不同的coon,因為如果查找時用的coon和插入時的一樣的話,事務沒有提交也是可以查到的,因為用到的對象是同一個,里面已經有數據了?。。?!
回滾:假設A給B轉賬,A的money--,B的money++,兩者是一個整體。只有兩個操作都成功完成了,事務才會提交,否則都會回滾到原來的狀態。
在emp表里再添加一個salary屬性,把員工編號為1的員工的工資更改為1000,并且判斷員工編號為3的員工的工資是否大于5000,如果小于5000則加1000,這兩個操作為一個事務,要想事務能夠正常的提交的條件是員工編號為3的員工的工資小于5000,工資加了1000,此時員工編號為1的員工的工資更改為1000.如果員工編號為3的員工的工資大于5000則中斷程序,拋出異常,即操作2沒有順利完成,事務不應提交,而應該回滾到最初的狀態。
當然,不是所有的時候都需要恢復最初的狀態的,所以有時候就會用到自己設置回滾點,上述程序中注釋的部分就是自己添加了如果事務沒有正常提交想要恢復到程序執行到哪個位置的回滾點。
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(); //設置回滾點 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); } }}
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。