Spring学习笔记事务(一)
admin
2023-06-08 15:01:36
0

鲁春利的工作笔记,好记性不如烂笔头



事务是一系列操作组成的工作单元,是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务。
事务必需满足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配置方式)和编程式事务。

相关内容

热门资讯

神舟二十三号载人飞船发射升空 IT之家 5 月 24 日消息,刚刚,搭载神舟二十三号载人飞船的长征二号 F 遥二十三运载火箭在酒泉...
浙江上线“创新积分制”数智平台 来源:中国新闻网 中新网杭州5月24日电(鲍梦妮)5月24日,在2026年浙江省“全国科技活动周”上...
国内知名冷门绝学大咖齐聚深圳,... 本文转自【光明日报】; 5月22日上午,作为2026文化强国建设高峰论坛重要组成部分,首届冷门绝学传...
神舟二十三号载人飞行任务航天员... 据微信公众号“中国载人航天”消息,5月24日20时16分,神舟二十三号载人飞行任务航天员乘组出征仪式...
以总理:同特朗普一致认为须消除... 新华社耶路撒冷5月24日电(记者庞昕熠 王卓伦)以色列总理内塔尼亚胡24日在社交平台发文称,他和美国...
专家解读|秉持以人为本 统筹发... 随着大模型、生成式人工智能、智能体等技术的迭代创新,人工智能正在进入加速应用的新阶段,由此引发的社会...
原创 v... vivo在5月份的新机继续增加,比如vivo S60系列、vivo Y600 Turbo等,均在5月...
“暗面”之下:山西沁源82死矿... 山西省沁源县,留神峪煤矿。5月23日傍晚,作业现场已被拦住,你只能在矿区门口看到一道栅栏,背后封锁着...
神二十三发射任务将创下多个“首... 神舟二十三号即将发射,现场各项准备工作如何?凤凰卫视特派记者孙伟健从酒泉卫星发射中心发回现场报道。
山西留神峪煤矿事故调查:写满安... 5月22日19时29分,山西沁源的暮色尚未完全降临,有着45年开矿历史的通洲集团留神峪煤矿,骤然迎来...