鲁春利的工作笔记,好记性不如烂笔头
事务是一系列操作组成的工作单元,是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务。
事务必需满足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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。