您好,登錄后才能下訂單哦!
魯春利的工作筆記,好記性不如爛筆頭
事務是一系列操作組成的工作單元,是不可分割的,即要么所有操作都做,要么所有操作都不做,這就是事務。
事務必需滿足ACID(原子性、一致性、隔離性和持久性)特性,缺一不可:
原子性(Atomicity):即事務是不可分割的最小工作單元,事務內的操作要么全做,要么全不做;
一致性(Consistency):在事務執行前數據庫的數據處于正確的狀態,而事務執行完成后數據庫的數據還是處于正確的狀態;
隔離性(Isolation):并發事務執行之間無影響,在一個事務內部的操作對其他事務不會產生影響;
持久性(Durability):事務一旦執行成功,它對數據庫的數據的改變必須是永久的。
在實際項目開發中數據庫操作一般都是并發執行的,即有多個事務并發執行,并發執行就可能遇到問題,目前常見的問題如下:
更新丟失:兩個事務同時更新一行數據,最后一個事務的更新會覆蓋掉第一個事務的更新,從而導致第一個事務更新的數據丟失,這是由于沒有加鎖造成的;
臟讀:一個事務看到了另一個事務未提交的更新數據;
不可重復讀:在同一事務中,多次讀取同一數據卻返回不同的結果;也就是有其他事務更改了這些數據;
幻讀:一個事務在執行過程中讀取到了另一個事務已提交的插入數據;即在第一個事務開始時讀取到一批數據,但此后另一個事務又插入了新數據并提交,此時第一個事務又讀取這批數據但發現多了一條,即好像發生幻覺一樣。
為了解決這些并發問題,需要通過數據庫隔離級別來解決,在標準SQL規范中定義了四種隔離級別:
讀未提交(Read Uncommitted):最低隔離級別,一個事務能讀取到別的事務未提交的更新數據,很不安全,可能出現更新丟失、臟讀、不可重復讀、幻讀;
讀已提交(Read Committed):一個事務能讀取到別的事務提交的更新數據,不能看到未提交的更新數據,不可能可能出現丟失更新、臟讀,但可能出現不可重復讀、幻讀;
可重復讀(Repeatable Read):保證同一事務中先后執行的多次查詢將返回同一結果,不受其他事務影響,可能可能出現丟失更新、臟讀、不可重復讀,但可能出現幻讀;
序列化(Serializable):最高隔離級別,不允許事務并發執行,而必須串行化執行,最安全,不可能出現更新、臟讀、不可重復讀、幻讀。
數據庫事務類型有本地事務和分布式事務:
地事務:普通事務,單臺數據庫操作的ACID;
分布式事務:即跨越多臺同類或異類數據庫的事務。
Java事務類型有JDBC事務和JTA事務:
JDBC事務:通過Connection對象的控制來管理事務;
JTA事務:JTA指Java事務API(Java Transaction API),是Java EE數據庫事務規范, JTA只提供了事務管理接口,由應用程序服務器廠商(如WebSphere Application Server)提供實現,JTA事務比JDBC更強大,支持分布式事務。
在JDBC模式下,由程序員獲取數據庫連接、執行數據操作并控制事務,如:
package com.invicme.apps.tx.jdbc; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; /** * * @author lucl * * JDBC數據庫連接的工具類 * */ public class DbHelper { /** * */ private static String driver = null; private static String url = null; private static String username = null; private static String password = null; static { // 該properties配置文件與工具類位于同一目錄下 InputStream in = DbHelper.class.getResourceAsStream("jdbc.properties"); Properties props = new Properties(); try { props.load(in); } catch (IOException e) { e.printStackTrace(); } driver = props.getProperty("jdbc.driver"); url = props.getProperty("jdbc.url"); username = props.getProperty("jdbc.username"); password = props.getProperty("jdbc.password"); } /** * 獲取數據庫連接 * * @return */ public Connection getCon () { Connection con = null; try { Class.forName(driver).newInstance(); // MYSQL驅動 con = DriverManager.getConnection(url, username, password); // 鏈接本地MYSQL } catch (Exception e) { con = null; e.printStackTrace(); } return con; } /** * 關閉數據庫連接 * * @param con * @throws SQLException */ public void closeCon (Connection con) throws SQLException { if (null != con) { con.close(); } } }
單元測試類
package com.test.apps.spring.tx; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.sql.Statement; import org.apache.log4j.Logger; import org.junit.Test; import com.invicme.apps.tx.jdbc.DbHelper; /** * * @author lucl * */ public class TestJdbcTransaction { // private static final Logger logger = Logger.getLogger(TestJdbcTransaction.class); @Test public void testJdbcTransaction () { DbHelper dbHelper = new DbHelper(); Connection con = dbHelper.getCon(); try { DatabaseMetaData metaData = con.getMetaData(); /** * 數據庫基本信息 */ // MySQL String databaseProductName = metaData.getDatabaseProductName(); // 5.6.17-log String databaseProductVersion = metaData.getDatabaseProductVersion(); // 5 int databaseMajorVersion = metaData.getDatabaseMajorVersion(); // 6 int databaseMinorVersion = metaData.getDatabaseMinorVersion(); // MySQL 5.6.17-log logger.info(databaseProductName + " " + databaseProductVersion); // 5.6 logger.info(databaseMajorVersion + "." + databaseMinorVersion); int defaultTransactionIsolation = metaData.getDefaultTransactionIsolation(); // 事務隔離級別:2 logger.info(defaultTransactionIsolation); boolean supportsTransactions = metaData.supportsTransactions(); // 是否支持事務:true logger.info(supportsTransactions); String driverName = metaData.getDriverName(); String driverVersion = metaData.getDriverVersion(); int driverMajorVersion = metaData.getDriverMajorVersion(); int driverMinorVersion = metaData.getDriverMinorVersion(); // MySQL Connector Java logger.info(driverName); // mysql-connector-java-5.1.38 ( Revision: fe541c166cec739c74cc727c5da96c1028b4834a ) logger.info(driverVersion); // 5.1 logger.info(driverMajorVersion + "." + driverMinorVersion); } catch (SQLException e) { e.printStackTrace(); } try { // 關閉自動提交 con.setAutoCommit(false); Statement statement = con.createStatement(); statement.executeUpdate("update user set name='zhangsan' where name='lisi' "); // 事務提交 con.commit(); } catch (SQLException e) { e.printStackTrace(); // 事務回滾 try { con.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { try { dbHelper.closeCon(con); } catch (SQLException e) { e.printStackTrace(); } } } }
說明:
在JDBC2.0中,事務只能進行commit或rollback;而JDBC3.0引入了保存點特性,即允許將事務分割為多個階段。
Savepoint savepoint = con.setSavepoint("svpt"); // ...... con.rollback(savepoint);
Spring簡化了這個過程,提供了兩種事務控制方式,即:聲明式事務(注解方式和XML配置方式)和編程式事務。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。