世界萬事無簡單一說, 每一個事情基本上由多個小的事情來完成。有的事情會存在若小的事情不能同時完成的狀況就取消全部的小的事情,直至都完成達到預期的效果纔算完成!這樣就用到了事務操做。在全部的sql語句完成以前,若發生異常,則讓事務回滾到開始事務的時候,讓事務結束;而且讓已執行的sql語句做廢。可是鏈接數據庫的connnection與開啓事務的connnection必須是一個。這樣原來在dao層開啓鏈接必須到service層一塊兒執行了開啓事務和處理異常。html
1、事務:java
一件事情有n個組成單元 :要不這n個組成單元同時成功, 要不n個單元就同時失敗。就是將n個組成單元放到一個事務中!mysql
2、mysql事務:sql
一、默認的事務:一條sql語句就是一個事務 。默認就開啓事務並提交事務!數據庫
二、手動事務:併發
①、顯示的開啓一個事務:start transaction spa
此時進行的全部的修改都是內存裏修改的,具體數據庫內到底修改了沒仍是由是否提交來決定的!!!code
當提交了就表明數據庫修改了,當回滾了則沒有修改!htm
可是須要注意的是不管數據庫到底修改了沒有,只要執行了SQL語句,在庫內的表裏的自增的序號會自動被佔用了!!!對象
②、事務提交:commit
表明從開啓事務到事務提交中間的全部的sql都認爲有效! 真正的更新數據庫!
③、事務的回滾:rollback
表明事務的回滾--在其以前的全部的操做都做廢了!回滾到start的地方,同時當前的事務結束了!
3、JDBC事務操做:
一、默認是自動事務:
執行sql語句:executeUpdate() ---- 每執行一次executeUpdate方法 表明 事務自動提交(默認)<執行一句sql就是一個提交>
二、經過jdbc的API手動事務:
①、開啓事務:conn.setAutoCommit(false);
此爲設置爲自動提交(改成false,也就是改成手動提交)
②、提交事務:conn.commit();
③、回滾事務:conn.rollback(); (都被封裝成了方法)
注意:
控制事務的connnection必須是同一個!
執行sql的connection與開啓事務的connnection必須是同一個才能對事務進行控制!!!
public static void main(String[] args) { Connection conn = JDBCUtils.getConn(); Statement sta = null; // Statement sta = conn.createStatement(); String sql = "insert into account(aname,money) values('wangwu',1000)"; // 手動開啓事務 try { sta = conn.createStatement(); conn.setAutoCommit(false); sta.executeUpdate(sql); } catch (SQLException e) { // 回滾 try { conn.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } finally {//注意好位置 try { // 提交事務 conn.commit(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }
注:
Statement 接口提供了執行語句和獲取結果的基本方法。PreparedStatement 接口添加了處理 IN 參數的方法;而 CallableStatement 添加了處理 OUT 參數的方法。
4、DBUtils事務操做:
QueryRunner:
一、有參構造:QueryRunner runner = new QueryRunner(DataSource dataSource);
有參構造將數據源(鏈接池)做爲參數傳入QueryRunner,QueryRunner會從連 接池中得到一個數據庫鏈接資源操做數據庫,因此直接使用無Connection參數 的update方法便可操做數據庫
二、無參構造:QueryRunner runner = new QueryRunner();-
無參的構造沒有將數據源(鏈接池)做爲參數傳入QueryRunner,那麼咱們在使 用QueryRunner對象操做數據庫時要使用有Connection參數的方法
<由於有參沒法保證connection的惟一性,因此須要無參構造---而且須要手動獲取鏈接>
public class MyDBUtils { public static final String DRIVER = "com.mysql.jdbc.Driver"; public static final String URL = "jdbc:mysql://localhost:3306/java0603?useUnicode=true&characterEncoding=UTF-8"; public static final String USERNAME = "root"; public static final String PASSWORD = "123456"; /* * 建立鏈接池BasicDataSource */ public static BasicDataSource dataSource = new BasicDataSource(); //靜態代碼塊(優先只執行一次) static { //對鏈接池對象 進行基本的配置 dataSource.setDriverClassName(DRIVER); // 這是要鏈接的數據庫的驅動 dataSource.setUrl(URL); //指定要鏈接的數據庫地址 dataSource.setUsername(USERNAME); //指定要鏈接數據的用戶名 dataSource.setPassword(PASSWORD); //指定要鏈接數據的密碼 } /* * 返回鏈接池對象 */ public static DataSource getDataSource(){ return dataSource; } //返回一個鏈接對象 public static Connection getConn(){ Connection conn=null; try { conn= dataSource.getConnection(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } }
public static void main(String[] args) { //獲取鏈接池對象 QueryRunner qr = new QueryRunner(); //獲取鏈接對象 Connection conn = MyDBUtils.getConn(); String sql = "update account set money = money - ? where aname = ?"; try { //開啓事務 conn.setAutoCommit(false); qr.update(conn,sql,100,"zhangsan"); } catch (SQLException e) { //回滾事務 try { conn.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // TODO Auto-generated catch block e.printStackTrace(); }finally{ //提交事務 try { conn.commit(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
5、轉帳實例:
public class AccountDao { // 轉出--須要和service層同一個conn 因此須要傳值 public void Moneyout( Connection conn,String out, double money) throws SQLException { QueryRunner qr = new QueryRunner(); String sql = "update account set money = money - ? where aname = ?"; qr.update(conn, sql, money, out); } // 轉入 public void Moneyin(Connection conn,String in, double money) throws SQLException { QueryRunner qr = new QueryRunner(); String sql = "update account set money = money + ? where aname = ?"; qr.update(conn, sql, money, in); } }
public class AccountService { private AccountDao accountDao = new AccountDao(); // 轉帳--須要在此層處理事務,因此在此層得到CONN,再把這個參數傳給dao層 public boolean transfer(String out, String in, double money) { // 定義變量 boolean flag = true; Connection conn = MyDBUtils.getConn(); try { // 開啓事務 conn.setAutoCommit(false); accountDao.Moneyout(conn, out, money); accountDao.Moneyin(conn, in, money); } catch (SQLException e) { flag = false; // 若是出現異常,則flag返回一個false // 回滾(當try出現異常後會執行catch,而後回滾到開啓事務以前) try { conn.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } finally { // 提交 try { conn.commit(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return flag; } }
public class TransferServlet extends HttpServlet { private AccountService accountService =new AccountService(); public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲取頁面上值 //解決中文亂碼 request.setCharacterEncoding("UTF-8"); //獲取轉出帳戶 String out = request.getParameter("out"); //獲取轉入帳戶 String in = request.getParameter("in"); //獲取轉帳金額(從前臺獲取的都是String類型) String moneyStr = request.getParameter("money"); //將字符串金額轉成double double money = Double.parseDouble(moneyStr); //調用 Service層的轉帳方法 boolean flag = accountService.transfer(out, in, money); //解決response亂碼 response.setContentType("text/html;charset=utf-8"); if(flag){ response.getWriter().write("轉帳成功"); }else{ response.getWriter().write("轉帳失敗"); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
5、事務的特性和隔離級別:事務的特性ACID
一、原子性(Atomicity)
原子性是指事務是一個不可分割的工做單位,事務中的操做 要麼都發生,要麼都不發生。
二、一致性(Consistency)
一個事務中,事務先後數據的完整性必須保持一致。
三、隔離性(Isolation)
多個事務,事務的隔離性是指多個用戶併發訪問數據庫時,一個用戶的事務不能被其它用戶的事務所幹擾,多個併發事務之間數據要相互隔離。
四、持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變 就是永久性的,接下來即便數據庫發生故障也不該該對其有任何影響。