Jedis事務
我們使用JDBC串連Mysql的時候,每次執行sql語句之前,都需要開啟事務;在MyBatis中,也需要使用openSession()來擷取session事務對象,來進行sql執行、查詢等操作。當我們對資料庫的操作結束的時候,是事務對象負責關閉資料庫連接。
事務對象用於管理、執行各種資料庫操作的動作。它能夠開啟和關閉資料庫連接,執行sql語句,復原錯誤的操作。
我們的Redis也有交易管理對象,其位於redis.clients.jedis.Transaction下。
Jedis事務的相關代碼:
package cn.com.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class Test7 { public static void main(String[] args) { Jedis jedis = new Jedis("192.168.248.129",6379); Transaction transaction=jedis.multi();//返回一個事務控制對象 //預先在事務對象中裝入要執行的操作 transaction.set("k4", "v4"); transaction.set("k5", "v5"); transaction.exec();//執行 } }
我們查看一下redis:
探索資料已經加入進去
我們把k4的value和k5的value改為“v44”和“v55”,然後在transaction.exec()語句後加入transaction.discard()語句:
package cn.com.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class Test7 { public static void main(String[] args) { Jedis jedis = new Jedis("192.168.248.129",6379); Transaction transaction=jedis.multi();//返回一個事務控制對象 //預先在事務對象中裝入要執行的操作 transaction.set("k4", "v44"); transaction.set("k5", "v55"); transaction.discard();//復原 } }
會探索資料插入操作被復原,redis中那兩個值未被改變:
我們類比一個刷一次信用卡的交易,使用redis的事務來處理一些邏輯:
package cn.com.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class TestTransaction { //類比信用卡消費和還款 public static void main(String[] args) { TestTransaction t = new TestTransaction(); boolean retValue = t.transMethod(100); if(retValue){ System.out.println("使用信用卡消費成功!"); }else{ System.out.println("使用信用卡消費失敗!"); } } /** * 通俗點講,watch命令就是標記一個鍵,如果標記了一個鍵, * 在提交事務前如果該鍵被別人修改過,那事務就會失敗,這種情況通常可以在程式中 * 重新再嘗試一次。 * * 首先標記了balance,然後檢查餘額是否足夠,不足就取消標幟,並不做扣減; * 足夠的話,就啟動事務進行更新操作。 * 如果在此期間鍵balance被其他人修改,拿在提交事務(執行exec)時就會報錯, * 程式中通常可以捕獲這類錯誤再重新執行一次,直到成功。 * */ private boolean transMethod(int amount) { System.out.println("您使用信用卡預付款"+amount+"元"); Jedis jedis = new Jedis("192.168.248.129",6379); int balance = 1000;//可用餘額 int debt;//欠額 int amtToSubtract = amount;//實刷額度 jedis.set("balance", String.valueOf(balance)); jedis.watch("balance"); //jedis.set("balance", "1100");//此句不該出現,為了類比其他程式已經修改了該條目 balance = Integer.parseInt(jedis.get("balance")); if(balance < amtToSubtract){//可用餘額小於實刷金額,拒絕交易 jedis.unwatch(); System.out.println("可用餘額不足!"); return false; }else{//可用餘額夠用的時候再去執行計費操作 System.out.println("計費transaction事務開始執行..."); Transaction transaction = jedis.multi(); transaction.decrBy("balance",amtToSubtract);//餘額減去amtToSubtract的錢數 transaction.incrBy("debt", amtToSubtract);//信用卡欠款增加amtToSubtract的錢數 transaction.exec();//執行事務 balance = Integer.parseInt(jedis.get("balance")); debt = Integer.parseInt(jedis.get("debt")); System.out.println("計費transaction事務執行結束..."); System.out.println("您的可用餘額:"+balance); System.out.println("您目前欠款:"+debt); return true; } } }
此代碼就是類比使用者使用信用卡刷了100元的東西,此時應該減去信用卡的可用餘額100元,增加100元的欠款。
運行結果:
redis的結果:
證明我們的操作是成功的。
加watch命令是為了在事務執行的過程中,防止其它的操作打斷事務,或者是影響事務的計算結果,導致“幻讀”、“髒資料”等異常情況的發生。watch命令建立了一個鍵,一旦發現執行過程中該鍵被別人修改過,那事務就會失敗,程式中通常可以捕獲這類錯誤再重新執行一次,直到成功。所以watch命令可以保證資料的同步安全。
為了證明watch命令的用途,我們把上面代碼裡面的jedis.set("balance", "1100");注釋釋放,然後transMethod方法拋出打斷異常:throws InterruptedException,main方法捕獲打斷異常,然後彈出相應警告框。
package cn.com.redis; import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class TestTransaction { //類比信用卡消費和還款 public static void main(String[] args) { TestTransaction t = new TestTransaction(); boolean retValue=false; boolean Interrupted = false; try { retValue = t.transMethod(100); } catch (InterruptedException e) { Interrupted = true; System.out.println("事務被打斷,請重新執行!"); }finally{ if(retValue){ System.out.println("使用信用卡消費成功!"); }else{ if(!Interrupted){ System.out.println("使用信用卡消費失敗!餘額不足!"); } } } } /** * 通俗點講,watch命令就是標記一個鍵,如果標記了一個鍵, * 在提交事務前如果該鍵被別人修改過,那事務就會失敗,這種情況通常可以在程式中 * 重新再嘗試一次。 * * 首先標記了balance,然後檢查餘額是否足夠,不足就取消標幟,並不做扣減; * 足夠的話,就啟動事務進行更新操作。 * 如果在此期間鍵balance被其他人修改,拿在提交事務(執行exec)時就會報錯, * 程式中通常可以捕獲這類錯誤再重新執行一次,直到成功。 * */ private boolean transMethod(int amount) throws InterruptedException{ System.out.println("您使用信用卡預付款"+amount+"元"); Jedis jedis = new Jedis("192.168.248.129",6379); int balance = 1000;//可用餘額 int debt;//欠額 int amtToSubtract = amount;//實刷額度 jedis.set("balance", String.valueOf(balance)); jedis.watch("balance"); jedis.set("balance", "1100");//此句不該出現,為了類比其他程式已經修改了該條目 balance = Integer.parseInt(jedis.get("balance")); if(balance < amtToSubtract){//可用餘額小於實刷金額,拒絕交易 jedis.unwatch(); System.out.println("可用餘額不足!"); return false; }else{//可用餘額夠用的時候再去執行計費操作 System.out.println("計費transaction事務開始執行..."); Transaction transaction = jedis.multi(); transaction.decrBy("balance",amtToSubtract);//餘額減去amtToSubtract的錢數 transaction.incrBy("debt", amtToSubtract);//信用卡欠款增加amtToSubtract的錢數 List<Object> result = transaction.exec();//執行事務 if(result==null){//事務提交失敗,說明在執行期間資料被修改過 System.out.println("計費transaction事務執行中斷..."); throw new InterruptedException(); }else{//事務提交成功 balance = Integer.parseInt(jedis.get("balance")); debt = Integer.parseInt(jedis.get("debt")); System.out.println("計費transaction事務執行結束..."); System.out.println("您的可用餘額:"+balance); System.out.println("您目前欠款:"+debt); return true; } } } }
再運行一下,看一下效果:
這就說明了,如果在watch命令執行後和事務提交之前,如果資料發生了修改操作,事務執行就不會成功,此舉保證了資料的安全性。
以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。