Spring.NET学习笔记16——事务管理(应用篇) Level 200

【信息科学与工程学】计算机科学与自动化——第八十一篇 Java分布式软件高并发/高可用算法01 4. 消息队列(如Kafka的Topic分区,本质是范围)。3. 若是ACCEPT事件,bossGroup线程接受连接,创建SocketChannel,并将其注册到workerGroup的一个EventLoop的Selector上,关注READ事件。1. 分布式缓存分片(如Redis Cluster)。4. 分布式文件存储(如Ceph CRUSH)。6. 调用栈的模拟(如协程)。3. 服务网格(如Istio)的基础。2. 本地缓存与集中式缓存(如Redis)的一致性维护。3. 静态数据(如配置)缓存。 阅读详情
 目前有很多种数据访问技术。在.NET FCL中,有三类API可以执行事务管理,分别是ADO.NET、企业服务和 System.Transactions 。其它的数据访问技术,如对象关系映射(object relational mappers)和结果集映射(result-set mapping)等等的应用也很广泛,每种技术也都有自己的事务管理API。事务管理的代码一般是直接和各种事务API绑定在一起的,所以在开发时必须根据所用的具体技术来决定采用哪种API。但是,这种代码与事务API的紧耦合决定了很难通过简单的重构来解决更换数据访问技术的问题。而Spring.NET的事务框架允许在各种数据访问技术之上使用相同的API。通过配置或者集中的编程方式,可以很容易的更换后台事务API,而不需要对代码进行“大修”。

  我们可以用业界公认的最佳方式来建立一种数据访问机制。Martin Fowler的著作《Patterns of Enterprise Application Architecture》讲到了许多在实际应用中非常成功的数据访问方法。其一便是在应用程序架构中引入一个数据访问层。数据访问层不仅要考虑到与不同的数据库和数据访问技术的兼容性,而且职责要严格限制在数据访问功能上。数据访问层应该只包含数据访问对象(DAO)以及“创建/获取/更新/删除”(CRUD,Create/Retrieve/Update/Delete)的操作,不应该涉及任何业务逻辑。业务逻辑应该位于单独的业务服务层,并且需要与一或多个DAO协作来完成高层次的用户功能。

为了在事务中“要么全执行要么全不执行”这些用户功能,事务环境(transaction context)就应该由业务服务层(或某个“更高”的层次)控制。在实现上,一个很重要的细节是如何让DAO了解在其它层次中开始的“外部”事务。如果让DAO自己负责连接和事务的管理,就把问题看的过于简单化了,因为此时每个DAO都会执行自己的事务/资源管理,所以无法在同一事务中执行多个DAO操作。我们需要一种有效的手段,将连接/事务成对的从业务服务层传递给DAO。方法有很多种,最不具侵入性的就是将连接/事务作为方法参数显式的传递给DAO。另一种方法是将连接/事务放在线程本地存储内。不管使用哪种方法,只要在用ADO.NET,就必须得自己创建一个基础框架来完成这个任务。

  但是,等一下,企业服务不是能解决这个问题吗——还有System.Transactions命名空间呢?关于这个嘛,答案是对...也不对。企业服务确实能够让我们在事务环境中使用“原生”的ADO.NET在同一事务中执行多个DAO操作。但它的缺点是必须通过MS-DTC(Microsoft Distributed Transaction Coordinator)使用分布式(全局的)事务。如果只为了使用全局事务就必须依赖MS-DTC,那应用程序在性能上就会大打折扣了。

使用.NET2.0新增System.Transactions命名空间下的TransactonScope类时,也有相同的问题。TransactonScope类的目的实际上是——用using语句使一段代码成为事务性代码。只要访问事务性的资源,using语句中的普通ADO.NET代码就会在一个ADO.NET本地事务中运行。但是,System.Transactions(和数据库)的“神奇“之处在于,如果需要访问第二个事务性资源,本地事务就会升级为分布式事务。这个过程叫做PSPE(Promotable Single Phase Enlistment)。此外,还需提醒读者:在同一个数据库上使用同一连接字符串打开第二个连接,就会使本地事务升级为分布式事务。所以,如果每个DAO都执行自己的连接管理,那就完了:本地事务会突然升级为分布式事务!如果应用程序只使用一个数据库,要想避免这个问题,就必须将唯一的连接对象传递给系统中的所有DAO。另外要注意的是某些数据库不支持PSPE,就算用单个数据库连接也会使用分布式事务(比如Oracle)。

Spring.NET的声明式事务管理功能非常强大。在数据库事务领域,讨论声明式事务管理的话题并不是很多,因为现在开发人员已经可以不再直接用凌乱繁复的事务API来进行事务管理了,而是可以通过在类和方法上应用某些特性来进行。但是在FCL中,只有企业服务提供了这一功能。Spring.NET则填补了这个空白——不管使用哪种事务管理技术:ADO.NET,还是(最常用的)System.Transactions,都可以使用声明式的事务管理。另外,企业服务也有自己的问题,举例来说,如果需要为查询/读取操作和创建/更新/删除操作设置不同的隔离等级,就必须将这两组操作分隔在不同的类中实现,因为在企业服务中,声明式事务的元数据只对类有效。但总的来说,企业服务,特别是在XP sp2和Server 2003中新出现的“无组件服务”,还有在应用程序进程内驻留的功能都是相当不错的。不过,虽然有这些优点,企业服务仍尚未在开发社区中引起很大的关注。

Spring.NET事务管理的宗旨,就是要减轻FCL或第三方数据访问技术给开发人员带来的这些“痛苦”。它支持声明式事务管理,可以用配置方式获取事务选项的元数据——目前支持两种声明方式:在代码中用.NET特性声明;在IoC容器中用XML声明。

最后,Spring.NET事务管理还允许在同一事务中使用不同的数据访问技术——例如混合使用ADO.NET和NHibernate。

好了,再讲估计就有点烦人了,现在我们来看代码。

  在Spring.NET中,提供了以下实现类:

    AdoPlatformTransactionManager- 基于本地ADO.NET的事务。

    ServiceDomainPlatformTransactionManager- 由企业服务提供的分布式事务管理器。

    TxScopePlatformTransactionManager- 由System.Transactions提供的本地/分布式的事务管理器。

  

  ITransactionDefinition接口封装了以下信息:

    Isolation:该事务对其它事务操作的隔离级别。例如,用来表示某个事务是否能看到其它事务写入的、但尚未提交的信息。

    Propagation:一般情况下,TransactionScope内的代码都会在为其指定的事务中运行。但是,该属性可用来设置如果某个事务环境已经存在时,该事务内的方法是否要执行:比如说,是简单的让它在现有事务中继续运行呢(这是一般情况),还是挂起现有事务然后创建一个新事务来运行。

    Timeout:在超时(并且被事务基础框架自动回滚之前)前该事务可以运行多久。

    Read-only状态:只读的事务不会修改任何数据。在某些情况下(比如使用NHibernate时),只读事务能显著提高性能。

 

 

   让我们从实现代码中学习Spring.NET事务管理的机制。
   准备条件:数据中建了2张表,如图1

UserTable为存储用户信息的表,AccountTable为存储用户账号的信息。

数据库访问层:


    public interface IAccountDao
    {
        
void Create(string name, string userName);

        
void Delete(string userName);
    }
    
public class AccountDao : AdoDaoSupport, IAccountDao
    {
        
public void Create(string name, string userName)
        {
            AdoTemplate.ExecuteNonQuery(CommandType.Text,
                String.Format(
"INSERT INTO AccountTable (UserName,    AccountName) VALUES ('{0}', '{1}')", userName, name));
        }

        
public void Delete(string userName)
        {
            AdoTemplate.ExecuteNonQuery(CommandType.Text,
                String.Format(
"DELETE FROM AccountTable WHERE UserName = '{0}'", userName));
        }
    }

    public interface IUserDao
    {
        
void Create(string name, int age);

        
void Delete(string name);

        DataSet Get(
string name);
    }

    
public class UserDao : AdoDaoSupport, IUserDao
    {
        
public void Create(string name, int age)
        {
            AdoTemplate.ExecuteNonQuery(CommandType.Text,
                
string.Format("INSERT INTO UserTable (UserName,    UserAge) VALUES ('{0}', {1})", name, age));
        }

        
public void Delete(string name)
        {
            AdoTemplate.ExecuteNonQuery(CommandType.Text,
                
string.Format("DELETE FROM UserTable WHERE UserName = '{0}'", name));
        }

        
public DataSet Get(string name)
        {
            
return AdoTemplate.DataSetCreate(CommandType.Text, 
                
string.Format("SELECT * FROM UserTable WHERE UserName = '{0}'", name));
        }
    }

 

  业务处理层:当我们插入用户信息后,需要为这个用户建立一个账号,当我们删除用户时,需要将帐号信息一起删除。这里我们就会用到了事务。


    public interface IUserService
    {
        
void SaveData(string name, int age, string accountName);

        
void DeleteData(string name);

        DataSet Get(
string name);
    }

    
public class UserService : IUserService
    {
        
public IUserDao UserDao { get; set; }

        
public IAccountDao AccountDao { get; set; }

        [Transaction]
        
public void SaveData(string name, int age, string accountName)
        {
            UserDao.Create(name, age);
            AccountDao.Create(accountName, name);
        }

        [Transaction]
        
public void DeleteData(string name)
        {
            UserDao.Delete(name);
            AccountDao.Delete(name);
        }

        [Transaction(ReadOnly 
= true)]
        
public DataSet Get(string name)
        {
            
return UserDao.Get(name);
        }
    }

 

  配置:


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net" 
         xmlns:db
="http://www.springframework.net/database" 
         xmlns:tx
="http://www.springframework.net/tx">

  
<db:provider id="DbProvider"
                provider
="SqlServer-1.1"
                connectionString
="Server=(local);Database=SpringLesson16;Uid=sa;Pwd=;Trusted_Connection=False"/>
  
  
<object id="userDao" type="Dao.UserDao, Dao">
    
<property name="AdoTemplate" ref="adoTemplate"/>
  
</object>

  
<object id="accountDao" type="Dao.AccountDao, Dao">
    
<property name="AdoTemplate" ref="adoTemplate"/>
  
</object>

  
<object id="userService" type="Service.UserService, Service">
    
<property name="UserDao" ref="userDao"/>
    
<property name="AccountDao" ref="accountDao"/>
  
</object>

  
<object id="adoTemplate" type="Spring.Data.Core.AdoTemplate, Spring.Data">
    
<property name="DbProvider" ref="DbProvider"/>
    
<property name="DataReaderWrapperType" value="Spring.Data.Support.NullMappingDataReader, Spring.Data"/>
  
</object>

  
<!--事务管理器-->
  
<object id="transactionManager"
         type
="Spring.Data.Core.AdoPlatformTransactionManager, Spring.Data">
    
<property name="DbProvider" ref="DbProvider"/>
  
</object>


  
<!--事务切面-->
  
<tx:attribute-driven/>

</objects>

 


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
  
<configSections>
    
<sectionGroup name="spring">
      
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
      
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
      
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
    
</sectionGroup>
  
</configSections>

  
<spring>
    
<parsers>
      
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data"/>
      
<parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data"/>
    
</parsers>
    
<context>
      
<resource uri="assembly://Dao/Dao/Dao.xml"/>
    
</context>

  
</spring>

</configuration>

 

  我们新建一个单元测试:


    [TestFixture]
    
public class TransactionTest
    {
        [Test]
        
public void AdoTransaction()
        {
            IApplicationContext ctx 
= ContextRegistry.GetContext();
            IUserService service 
= (IUserService)ctx.GetObject("userService");
            service.SaveData(
"刘冬", 26, "1233456");
        }
    }

 

输出效果:两条数据已经插入数据库(图2)
图2

 

  我们修改一下UserService的DeleteData方法,在调用 UserDao.Delete(name)以后抛出异常,测试数据是否回滚。

 


        [Transaction]
        
public void DeleteData(string name)
        {
            UserDao.Delete(name);
            
new Exception("测试数据是否回滚");
            AccountDao.Delete(name);
        }

 

   如果数据没有回滚,则我们得到的数据为UserTable表中没有数据,AccountTable表中有数据。

 

TransactionTest

 

输出效果:图3
图3

 

  我们发现数据已经回滚。

  Spring.NET框架帮我们很好的管理了事务。但在我们的编码过程中经常使用到try...catch语句。要是在DeleteData方法中加入try...catch语句会回滚吗?我们修改一下UserService的SaveData方法

SaveData

 

当前的数据库AccountTable表中已经存在字段为AccountName的“123456”记录。因为AccountName字段是主键,我再插入一条数据就会出现异常。

AdoTransaction

 

输出效果:图4
图4

  我们发现在使用try...catch语句以后并没有回滚。从以上的效果中,我们可以得出结论:使用try...catch的异常叫作“运行期异常”,Spring.NET的事务管理默认是不对运行期异常回滚的。

  如果要实现没有try...catch语句不回滚事务,我们需要在方法上标记[Transaction(NoRollbackFor = new Type[] { typeof(Exception) })]。NoRollbackFor 属性是Type数组,意思是运到Exception异常就不回滚,您也可以在增加其它的异常条件,如typeof(ArithmeticException)。这样,当遇到标有包含NoRollbackFor 属性的异常时,就不进行回滚。

  代码下载

 

  参考Spring.NET中文手册

 

 

Spring.NET事务管理机制

原文:http://www.cnblogs.com/GoodHelper/archive/2009/11/16/SpringNet_Transaction.html

  返回目录

SSM+MySQL学校访客系统:教学级Java Web工程实践指南 Java Web开发是计算机专业核心能力,而SSM(Spring+SpringMVC+MyBatis)框架与MySQL数据库构成企业级轻量应用的经典技术组合。其原理在于分层解耦——Spring管理对象生命周期与事务,SpringMVC处理请求路由与视图响应,MyBatis实现面向POJO的SQL映射;MySQL则提供ACID保障、索引优化与多条件查询支持。该组合具备极高的教学适配性:配置显式、错误直观、资料丰富,能有效承载从需求分析、数据库设计、三层编码到部署调试的完整工程链路。典型应用场景包括校园访客登记 阅读详情

相关推荐

Spring.NET学习笔记16——事务管理Demo源码

Spring.NET事务管理机制 原文:http://www.cnblogs.com/GoodHelper/archive/2009/11/16/SpringNet_Transaction.html

Spring.NET教程(十六)事务管理(应用篇)

目前有很多种数据访问技术。在.net FCL中,有三类API可以执行事务管理,分别是ADO.NET、企业服务和System.Transactions。其它的数据访问技术,如对象关系映射(object relational mappers)和结果集映射(result-set mapping)等等的应用也很广泛,每种技术也都有自己的事务管理API。事务管理的代码一般是直接和各种事务AP...

lzhdim的专栏 388

SQL语句自动化形成与Dao层模型

插件中封装了一系列数据库操作的类,根据实体模型自动化生成插入、更新、删除、查询语句,Dao层通用函数封装,使用此插件基本上只用写少量的数据库层代码即可完成对数据库的操作。

Spring.net--很棒的事务处理

1--Case 比如t_Order订单表1,t_OrderDetail订单明细表2 下一张订单会往表1插入一条数据,表2会插入多行数据 使用Spring.net事务管理 例如 ---Order---订单主信息------- public class OrderBIL { private void InsertOrderHeader(Order order)   { ...

weixin_30906671的博客 180

Spring.NET学习笔记17——事务传播行为(基础篇)代码下载

Spring.NET事务配置模板。 原文出处:http://www.cnblogs.com/GoodHelper/archive/2009/11/16/SpringNet_Transaction.html

遥感图像语义分割的全流程实现(Python代码)

本节介绍了加载遥感 RGB 图像和对应的单通道掩膜(mask)。掩膜中每个像素值为类别索引(如0=背景,1=植被,2=建筑),通过 transform 统一缩放(256×256)和归一化(ImageNet 均值/标准差),确保输入模型的一致性。# 自定义数据集类(适配遥感RGB+单通道掩膜)mask = Image.open(mask_path).convert("L") # 单通道掩膜return image, mask.squeeze(0).long() # 返回RGB图像和类别索引。

qq_42679807的博客 1852

.net Framework Class Library(FCL)

from:http://msdn.microsoft.com/en-us/library/ms229335.aspx 我们平时在VS.net里引用的那些类库就是从这里来的 The .NET Framework class library is a library of classes, interfaces, and value types that provides acce...

weixin_34391854的博客 443

【5G核心网】Packet Forwarding Model 数据包转发模型

CP 通过建立,修改,删除 PFCP 会话上下文,以及通过为每个 PFCP 会话赏析哪问提供 PDR FAR QER URR BAR,来控制 UP 面的数据包处理。 每一个 PDR 都包含 PDI(Packet Detection Information,包探测信息),一个或多个字段,与输入数据包进行匹配,并可能与以下规则相关联,从而提供了适用于与 PDI 匹配的数据包的指令集: - One FAR(包含了处理数据包的相关指令) an Apply Act...

zhonglinzhang 1万+

Spring.NET学习笔记17——事务传播行为(基础篇) Level 200

  上篇我们学习了Spring.NET的事务机制。回顾一下,实现事务需要在方法上标记[Transaction]。在很多情况下,事务往往与业务分离。Spring.NET提供了事务代理帮我们管理这些事务,我们可以通过TransactionProxyFactoryObject使用声明式事务。在很多情况下TransactionProxyFactoryObject比ProxyFactoryObject易用,...

技术资料收集站 282

innovus:Clock Gating Check设置与报告

看图后发现,在设置clock gating check之前报告也会有clock gating check的值,这个就是因为timing library中会像reg一样定 library setup time值,如下图。我们常用set_clock_gating_check -setup去过约束Place阶段reg2cgate的时序,以防CTS后由于tree不平导致的时序违例。我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧?

m0_61544122的博客 1012

【WiFi】T830 MT7992 WMM测试修改操作说明

WiFi WMM(Wi-Fi Multimedia)是Wi-Fi联盟推出的一项技术,用于优化无线网络中的多媒体内容传输。WMM通过优先处理不同类型的数据流来改善网络性能,特别是在网络拥堵时。:WMM通过提供服务质量支持,确保语音、视频和其他多媒体应用在无线网络中获得优先处理。这有助于减少延迟和抖动,改善用户体验。:WMM将数据流分为四个优先级队列:语音、视频、最佳效果(Best Effort)和背景(Background)。语音和视频流通常被赋予更高的优先级,以确保它们在网络中得到及时传输。

wgl307293845的博客 1020

工作中使用到的单词(软件开发)

1.エイリアス   alias 别名 powershell使用 get-help 命令时,会得到与此命令 相关(别名)的命令 2.プロキシ proxy 代 理 3.プロモート   promote 促进,提升   开发的系统新功能上线发布,有时会使用到这个词,比如 STプロモート 还有使用リリース这个词的。比如STリリース。个人认为两者含义相同。 4.見逃し み................................................................

sun0322 6万+

YOLOv11与CLIP融合的零样本目标检测技术:原理、实现与创新实践

YOLO(You Only Look Once)系列作为单阶段检测器的代表,以其实时高效的特性在工业界获得广泛应用。YOLOv11作为该系列的最新演进,引入了多项架构创新: C3K2模块:采用双小卷积核替代传统大卷积核,在保持感受野的同时减少计算量。当参数c3k=False时退化为YOLOv8的C2F模块,提供灵活的精度-效率权衡。 C2PSA(Convolutional block with Parallel Spatial Attention):通过并行空间注意力机制增强对微小目标的特征提取能力,这对

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 1068

国产化之达梦数据库性能优化方案

国产化之达梦数据库性能优化方案

7DGroup,让技术更有价值! 841

YOLOv8中的可变形卷积改进 | 从DCNv1到DCNv3的多位置替换可变形卷积保姆级实操

可变形卷积(Deformable Convolution)最早由Dai等人在2017年提出,其核心思想是在标准卷积操作的基础上,引入可学习的偏移量(offsets),使卷积核能够自适应调整其采样位置,从而增强模型对目标变形的感知能力。return x本文通过深入探讨可变形卷积在YOLOv8中的应用,详细介绍了DCNv1、DCNv2和DCNv3的原理和实现,并通过实验验证了其在目标检测任务中的有效性。未来,我们将继续探索更多优化策略和技术,提升YOLOv8模型的性能,以应对更复杂的目标检测任务。

一键难忘的博客 4051

基于遗传算法的码头泊位分配调度优化问题求解

例如,基因型为 [3, 1, 4, 2] 表示第一艘停靠在第三个泊位,第二艘停靠在第一个泊位,以此类推。同时,该码头有 n 艘船需要停靠,每艘船的货物数量、船长、船宽都不同,需要根据其特性来分配合适的泊位,并确定每个泊位的使用时间,以最大化码头的使用效率。码头的泊位分配调度是一个关键的物流问题,它涉及到船舶的进出港、货物的装卸和存储等多个方面,因此对其进行优化是至关重要的。根据得到的最优个体,可以得到每个泊位停靠的船只、使用时间等信息,从而确定码头的泊位分配调度方案。% 不同船只的货物数量。

uote_e的博客 1075
上一篇: 分布式事务管理System.Transactions.TransactionScope
下一篇: JAVA-tools
PAPALIAN
博客等级 码龄19年 42粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值