事务处理总结

(一)           数据库事务

 

1.       事务的定义:事务是由一系列操作序列构成的程序执行单元,这些操作要么都做,要么都不做,是一个不可分割的工作单位。

 

2.       事务的4个特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)、持久性(Duarability),简称ACID

 

2.1. 原子性(Atomic)

表示组成一个事务的多个数据库操作是一个不可分割的原子单元,只有所有的操作执行成功,整个事务才提交,事务中任何一个数据库操作失败,已经执行的任何操作都必须撤销,让数据库返回到初始状态。

 

2.2.  一致性(Consistency)

事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。

 

2.3.  隔离性(Isolation)

在并发数据操作时,不同的事务拥有各自的数据空间,它们的操作不会对对方产生干扰。准确地说,并非要求做到完全无干扰,数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱。多个事务并发执行时,一个事务的执行不应影响其他事务的执行。

 

2.4.  持久性(Duarability)

一旦事务提交成功后,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证能够通过某种机制恢复数据。一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

 

3.       数据并发问题

 

3.1.  脏读(Dirty Read)

事务A读到事务B尚未提交的数据,并基于这个数据进行后续操作。

 

3.2.  不可重复读(UnrepeatableRead)

事务A读取数据后,被事务B修改或删除,事务A再次读取时前后两次读取的数据不一致。

 

3.3.  虚读/幻象读(Phantomread)

事务A读取数据后,事务B新增了数据,事务A再次读取是前后两次读取的数据不一致。

 

3.4.  第一类更新丢失

事务A和事务B同时访问同一个数据,事务B先提交修改,事务A回滚操作。导致事务B的修改丢失。

 

3.5.  第二类更新丢失

事务A和事务B同时访问同一个数据,事务B先提交修改,事务A再提交。导致事务B的修改被覆盖。

 

4.       数据库锁机制

 

4.1.         按锁的作用范围分类

4.1.1.       行级锁/行锁定

4.1.2.       表级锁/表锁定

 

4.2.         按锁的排他性分类

4.2.1.       共享锁

4.2.2.       独占锁(排他锁)

 

4.3.         Oracle常用的5种锁定

 4.3.1.        行共享锁

允许多个会话共享锁定的行数据,但不允许对这些行的独占锁。例如select ...for update。行共享锁定并不防止对数据行进行更改的操作,但是可以防止其他会话获取独占性数据表锁定。

4.3.2.        行独占锁

对行进行独占,不允许其它的共享锁(表,行)、独占锁(表,行)和表共享行独占锁。例如insert,update。

4.3.3.        表共享锁

允许多个会话共享表数据,但不允许其它的独占锁(表,行)、表共享行独占锁。可以实现表级事务一致性。

4.3.4.        表独占锁

对表进行独占,不允许其它的共享锁(表,行)、读占锁(表,行)和表共享行独占锁。达到序列化操作级别。

4.3.5.        表共享行独占锁

允许多个会话共享表数据,但同一时刻只能有一个行独占锁。可以达到数据共享同时防止脏读、不可重复读、幻像读。表共享锁定可以让会话具有对表事务级一致性访问,因为其它会话在你提交或者回溯该事务并释放对该表的锁定之前不能更改这个被锁定的表(因为要修改表的记录,就必须获得行独占锁,但是共享锁会阻止独占锁的获取,这样原来其它正在读取表记录的事务就不会出现脏读、不可重复读、幻像读的情况了);表共享行独占锁与其不同则是多了一个行独占,这样效率更高。

 

5.       事务隔离级别

5.1.   TRANSACTION_NONE

说明不支持事务。

 

5.2.   TRANSACTION_READ_UNCOMMITTED

说明在提交前一个事务可以看到另一个事务的变化。这样脏读、不可重复读和虚读都是允许的。不允许第一类丢失更新、允许第二类丢失更新。

 

5.3.   TRANSACTION_READ_COMMITTED

说明读取未提交的数据是不允许的。这个级别仍然允许不可重复读和虚读产生。不允许第一类丢失更新、允许第二类丢失更新。

 

5.4.   TRANSACTION_REPEATABLE_READ

说明事务保证能够再次读取相同的数据而不会失败,但虚读仍然会出现。

不允许第一类丢失更新和第二类丢失更新。

 

5.5.   TRANSACTION_SERIALIZABLE

是最高的事务级别,它防止脏读、不可重复读和虚读。

不允许第一类丢失更新和第二类丢失更新。

 

6.       事务日志

事务日志是一个与数据库文件分开的文件。它存储对数据库进行的所有更改,并全部记录插入、更新、删除、提交、回退和数据库模式变化。事务日志还称作前滚日志或重做日志。

 

7.       事务回滚

事务是数据库更新操作的基本单位,事务回滚是指将该事务已经完成的对数据库的更新操作撤销。

 

 

(二)  Spring事务

1.  事务隔离

在TransactionDefinition接口中,定义了和java.sql.Connection接口中同名的4个隔离级别:ISOLATION_READ_UNCOMMITTED、ISOLATION_READ_COMMITTED、

ISOLATION_REPEATABLE_READ和ISOLATION_SERIALIZABLE。此外,还定义了一个默认的隔离级别:ISOLATION_DEFAULT,它表示使用底层数据库的默认隔离级别。

 

Spring隔离级别(表1)

TransactionDefinition隔离级别

ISOLATION_DEFAULT

-1

ISOLATION_READ_UNCOMMITTED

Connection.TRANSACTION_READ_UNCOMMITTED

ISOLATION_READ_COMMITTED

Connection.TRANSACTION_READ_COMMITTED

ISOLATION_REPEATABLE_READ

Connection.TRANSACTION_REPEATABLE_READ

ISOLATION_SERIALIZABLE

Connection.TRANSACTION_SERIALIZABLE

 

2.  事务传播

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:

 

事务传播行为类型表(表2)

事务传播行为类型

说明

PROPAGATION_REQUIRED

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY

使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER

以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

 

3.  事务超时

事务在超时前能运行多久,超过时间后,事务被回滚。有些事务管理器不支持事务过期的功能,这时,如果设置TIMEOUT_DEFAULT之外的其他值时,将抛出异常。

 

4.  只读状态

只读事务不修改任何数据,资源事务管理者可以针对可读事务应用一些优化措施,提高运行性能。只读事务在某些情况下(例如使用Hibernate时),是一种非常有用的优化,试图在只读事务中更改数据将引发异常。

 

(三)  项目中事务处理案例

1. 事务嵌套调用及多个事务处理

1.1.   需求

联强DBS项目的OSAP(订单处理共用模块)中有多个子方法中需要Spring事务管理机制来控制事务,并且子方法中也同样会调用有事务设定的公共方法。其中程式在执行过程中会先创建SA单,然后再去创建GG单,要求GG单创建过程中发生异常不影响SA单的生成。

 

1.2.   分析

起初想尝试寻找一种方法,把OSAP方法作为一个事务来处理(事务传播行为设定为PROPAGATION_NESTED),这样OSAP中嵌套调用的子方法会在同一个事务中执行。在程式中单GG单发生异常时通过异常捕获机制来控制异常的抛出,达到让程式继续执行的目的。但在程式的调试过程中发现,只要中有异常发生,整个事务就会回滚,以至于OSAP调用失败。

 

1.3.   原因

当在一个事务中,有任何一个方法在执行数据库操作时发生异常,Spring的事务机制会标志一个标志位(isRollbackOnly),这个标志位会最终导致整个事务的回滚(Transaction rolled back because it has been marked as rollback-only异常),即使通过异常捕获机制来捕获异常也无法实现事务的提交。

 

1.4.   解决方法

通过在执行产生SA单和GG单的方法上分别设置事务的传播行为为PROPAGATION_REQUIRES_NEW来实现需求,即作为两个事务来执行,这样即使在产生GG单时发生异常,也不会影响SA单的生成。

 

1.5.   总结

1.5.1 事务传播行为PROPAGATION_NESTED实现事务的嵌套,处理过程中的任何数据操作异常都会导致事务的回滚,保证了事务的一致性。但一些特殊的需求这种机制下无法实现,如实现事务的“部分回滚(注:假设一个事务只有部分实现提交,另一部分部分回滚的场景)”。

 

1.5.2 事务传播行为PROPAGATION_REQUIRES_NEW会新建一个事务,可以处理需求中“部分回滚”,其实就是通过这种机制可以将一个大的事务拆分成多个较小的事务来处理,其中一个事务的回滚不影响其他的事务的提交,以实现所谓事务的“部分回滚”的场景。这种情况下,事务的一致性也是满足的,但只针对于拆分后的每个事务单元。

 

备注:关于Transaction rolledback because it has been marked as rollback-only异常

该异常所在的类为Spring 的AbstractPlatformTransactionManager类,核心代码如下:

/**

   * This implementation of commit handlesparticipating in existing

   * transactions and programmatic rollbackrequests.

   * Delegates to <code>isRollbackOnly</code>, <code>doCommit</code>

   * and <code>rollback</code>.

   * @see org.springframework.transaction.TransactionStatus#isRollbackOnly()

   * @see #doCommit

   * @see #rollback

   */

  public final voidcommit(TransactionStatus status) throws TransactionException {

     if(status.isCompleted()) {

         throw newIllegalTransactionStateException(

                "Transaction is already completed - do not callcommit or rollback more than once per transaction");

     }

 

     DefaultTransactionStatusdefStatus = (DefaultTransactionStatus) status;

     if (defStatus.isLocalRollbackOnly()){

         if(defStatus.isDebug()) {

            logger.debug("Transactional code has requested rollback");

         }

         processRollback(defStatus);

         return;

     }

     if(!shouldCommitOnGlobalRollbackOnly() &&defStatus.isGlobalRollbackOnly()) {

         if(defStatus.isDebug()) {

            logger.debug("Global transaction is marked as rollback-only buttransactional code requested commit");

         }

         processRollback(defStatus);

         // Throw UnexpectedRollbackException only at outermosttransaction boundary

         // or if explicitly asked to.

         if(status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {

            throw new UnexpectedRollbackException(

                   "Transaction rolled back because it has been markedas rollback-only");

         }

         return;

     }

 

     processCommit(defStatus);

}

 

2. Spring抽象的 DAO 体系兼容多种数据访问技术(Hibernate+JdbcTemplate)

2.1.  需求

  在联强DBS项目中会发现很多程式中混合使用 Hibernate和JdbcTemplate数据访问技术。通过这种方式增加了对数据处理的灵活性,但这样也可能会带来一些问题。

 

2.2.  问题

     在OSAP模块中,当创建SA单后会调用一个Hibernate方法向订单看板表中保存数据。然后在后面的方法中有用JdbcTemplate方式去查询订单看板表中的数据,由于此时Hibernate缓存中的数据还没同步到数据库中,以至于JdbcTemplate没有查到相应的数据导致程式出错。默认情况下,Hibernate 要在事务提交时才将数据的更改同步到数据库中,由于程式还没有执行完,Hibernate对数据的更改仍然保持在缓存中没有提交,致使JdbcTemplate访问不到数据。

 

2.4. 解决方法

     调用Hibernate的 flush() 方法,将 Session 中的缓存同步到数据库中,因为这个操作将即时向数据库发送一条更新记录的 SQL 语句。这样再用JdbcTemplate去做查询时就可以查到相应的数据信息。

 

2.5.总结

    2.5.1.优点

        Hibernate 是非常优秀的 ORM 实现方案,在开发中可以很好的提升开发效率,而JdbcTemplate是Spring对底层JDBC的封装,具有很好的灵活性,可以处理比较复杂的SQL。通过两者的结合能够解决特定场景下的问题。

    2.5.2. 缺点

                在项目中混合使用 Hibernate 和 JdbcTemplate 数据访问技术也增加处理过程中的复杂度。其中可能会出现使用JdbcTemplate 访问数据时,Hibernate 的一级或二级缓存得不到同步,此外,一级缓存延迟数据同步机制可能会覆盖JdbcTemplate 数据更改的结果。

 

3. 数据连接泄露问题

如果直接从数据源获取连接,且在使用完成后不主动归还给数据源(调用 Connection.close()),则将造成数据连接泄漏的问题。

 

JdbcTemplate和Hibernate实现数据连接的几种常见方式(表3)

连接方式

说明

1.Connection conn = jdbcTemplate.getDataSource().getConnection()

创建一个新的数据连接

2.Connection conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource())

在事务上下文中从事务同步管理器中获取数据连接(获取事务上下文绑定的那个连接);否则创建一个新的数据连接

3.Session session = getHibernateTemplate().getSessionFactory().openSession()

创建一个连接并打开Session

4.Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();

获得当前连接会话

 

3.1 使用Spring定义的事务

当 Spring 事务方法运行时,就产生一个事务上下文,该上下文在本事务执行线程中针对同一个数据源绑定了一个唯一的数据连接(或其衍生品),所有被该事务上下文传播的方法都共享这个数据连接。这个数据连接从数据源获取及返回给数据源都在 Spring 掌控之中,不会发生问题。如果在需要数据连接时,能够获取这个被Spring 管控的数据连接,则使用者可以放心使用,无需关注连接释放的问题。

 

3.2  数据连接泄露的几种情况

 3.2.1. 表3方式1在没有使用完后没有及时释放连接会导致数据连接泄露。

程式如下:

public void method(Stringparams) {

        try {

            // 直接从数据源获取连接,后续程序没有显式释放该连接

            Connectionconn = jdbcTemplate.getDataSource().getConnection();

            Stringsql = "updatesql";

            jdbcTemplate.update(sql);

       

        } catch (Exception e) {

           e.printStackTrace();

}

 

3.2.2. 表3方式2在事务上下文中是安全的,Spring会控制数据连接。在没有事务上下文时和方式1一样,也会出现数据连接泄露。

 

3.2.3 表3方式3创建的Session在commit或rollback后必须手动关闭,否则也会出现数据连接泄露问题。

 

注:表3方式4创建的Session会绑定到当前的线程中去,所以Session在commit或rollback后会自动关闭。

 

3.3 总结

使用JdbcTemplate时如果直接获取 Connection,可能会造成连接泄漏。

为降低连接泄漏的可能,尽量使用 DataSourceUtils(Spring 提供了一个能从当前事务上下文中获取绑定的数据连接的工具类)获取数据连接。

 

备注:几种数据连接的声明

I.      openSession()

a.  类 org.hibernate.SessionFactory

b.  方法声明

Create databaseconnection and open a Session on it.

Returns:

Session

Throws:

HibernateException

public org.hibernate.classic.Session openSession()throws HibernateException;

 

II.    getCurrentSession()

a.  类 org.hibernate.SessionFactory

b.  方法声明

Obtains the current session. The definition of whatexactly "current" means controlled by theorg.hibernate.context.CurrentSessionContext impl configured for use.

Note that for backwards compatibility, if aorg.hibernate.context.CurrentSessionContext is not configured but a JTA org.hibernate.transaction.TransactionManagerLookupis configured this will default to the org.hibernate.context.JTASessionContextimpl.

Returns:

The current session.

Throws:

HibernateException Indicates an issue locating a suitablecurrent session.

public org.hibernate.classic.SessiongetCurrentSession() throws HibernateException;

 

III.   getConnection()

a.  类javax.sql.DataSource;

b.  方法声明

Attempt to establish adatabase connection.

Returns:

a Connection to thedatabase

Throws:

java.sql.SQLExceptionif a database-access error occurs.

ConnectiongetConnection() throws SQLException;

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在EBS(企业资源规划系统)中,事务处理与日记账之间存在着紧密的关联。事务处理是指企业日常的交易活动,包括采购、销售、库存管理、财务等方面的操作。而日记账是记录企业每一笔交易的账簿,用于追踪和记录企业的资金流动和交易情况。 首先,事务处理是通过EBS系统进行的,系统会记录下每一笔交易的详细信息,包括交易时间、交易类型、交易对象和交易金额等。这些信息会被传输到相应的日记账中,成为日记账的一部分。 其次,日记账是企业财务管理的重要组成部分。它记录了企业的各项交易活动,而这些交易活动都是通过事务处理在EBS系统中完成的。通过日记账,企业可以随时了解自己的财务状况,包括资金的流入流出、负债的增减等。日记账也是财务报表的来源之一,如资产负债表和利润表等。 同时,日记账还具有很强的稽核功能。通过比对事务处理和日记账之间的信息,可以确保交易的准确性和真实性。如果系统发生错误或者人为操作不当,日记账会反映出这些问题,从而可以对其进行纠正和调整。 总结来说,事务处理和日记账是EBS系统中紧密相关的两个方面。事务处理是日常交易活动的执行过程,而日记账则是对这些交易进行记录和追踪的工具。通过对两者的紧密关联,企业可以更加准确地了解自己的财务状况,并进行有效的财务管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值