嵌套事物总结

本文通过一个具体的案例详细介绍了在Java环境中如何正确地使用嵌套事务,包括如何处理异常以确保数据的一致性和事务的正确回滚。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事物成功总结
1、内外都无try Catch的时候,外部异常,全部回滚。
2、内外都无try Catch的时候,内部异常,全部回滚。
3、外部有try Catch时候,内部异常,全部回滚
4、内部有try Catch,外部异常,全部回滚
5、友情提示:外层方法中调取其他接口,或者另外开启线程的操作,一定放到最后!!!(因为调取接口不能回滚,一定要最后来处理)

总结:由于上面的异常被捕获导致,很多事务回滚失败。如果一定要将捕获,请捕获后又抛出 RuntimeException(默认为异常捕获RuntimeException
正确的嵌套事物实例
controller层
  1. package com.ycy.app;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.boot.SpringApplication;  
  5. import org.springframework.boot.autoconfigure.EnableAutoConfiguration;  
  6. import org.springframework.boot.autoconfigure.SpringBootApplication;  
  7. import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;  
  8. import org.springframework.context.annotation.ImportResource;  
  9. import org.springframework.web.bind.annotation.RequestMapping;  
  10. import org.springframework.web.bind.annotation.RestController;  
  11.   
  12. /**  
  13.  * Created by ycy on 16/7/19.  
  14.  */  
  15. @RestController  
  16. @SpringBootApplication  
  17. @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})  
  18. @ImportResource({"classpath:/applicationContext.xml"})  
  19. public class Application {  
  20.   @Autowired  
  21.   private TestBiz testBiz;  
  22.   
  23.   @RequestMapping("/")  
  24.   String home()  {  
  25.     System.out.println("controller 正常执行");  
  26.     try {  
  27.       testBiz.insetTes();  
  28.     } catch (Exception e) {  
  29.       System.out.println("controller 异常日志执行");  
  30.     }  
  31.   
  32.     return " 正常返回Hello World!";  
  33.   }  
  34.   
  35.   public static void main(String[] args) throws Exception {  
  36.     SpringApplication.run(Application.class, args);  
  37.   }  
  38. }  


外层biz层:
  1. package com.ycy.app;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.stereotype.Component;  
  5. import org.springframework.transaction.annotation.Transactional;  
  6.   
  7. import com.ycy.service.TestService;  
  8.   
  9. /**  
  10.  * Created by ycy on 16/7/20.  
  11.  */  
  12. @Component  
  13. public class TestBiz {  
  14.   @Autowired  
  15.   private TestService testService;  
  16.   
  17.   @Transactional  
  18.   public void insetTes() throws Exception {  
  19.     try {  
  20.       for (int j = 0; j < 8; j++) {  
  21.         testService.testInsert(j, j + "姓名");  
  22.         if (j == 3) {  
  23.           int i = 1 / 0;// 此处会产生异常  
  24.         }  
  25.       }  
  26.     } catch (Exception ex) {  
  27.       System.out.println("biz层异常日志处理");  
  28.       throw new RuntimeException(ex);  
  29.     }  
  30.   
  31.     System.out.println("biz层 正常执行");  
  32.   }  
  33. }  

内层service层
  1. package com.ycy.service.impl;  
  2.   
  3. import com.ycy.center.dao.entity.YcyTable;  
  4. import com.ycy.center.dao.mapper.YcyTableMapper;  
  5.   
  6. import org.springframework.beans.factory.annotation.Autowired;  
  7. import org.springframework.stereotype.Service;  
  8. import org.springframework.transaction.annotation.Transactional;  
  9.   
  10. /**  
  11.  * Created by ycy on 16/7/19.  
  12.  */  
  13. @Service  
  14. public class TestServiceImpl implements com.ycy.service.TestService {  
  15.   @Autowired  
  16.   private YcyTableMapper ycyTableMapper;  
  17.   @Transactional  
  18.   public void testInsert(int num, String name) throws Exception {  
  19.     try {  
  20.       YcyTable ycyTable = new YcyTable();  
  21.       ycyTable.setName(name);  
  22.       ycyTable.setNum(num);  
  23.       ycyTableMapper.insert(ycyTable);  
  24.       if (num== 3) {  
  25.         int i = 1 / 0;// 此处会产生异常  
  26.       }  
  27.     } catch (Exception ex) {  
  28.       System.out.println(num + "service异常日志处理");  
  29.         throw new RuntimeException(ex);  
  30.     }  
  31.     System.out.println(num + "service正常执行");  
  32.   }  

<think>我们正在讨论的是C#CAD二次开发中的事务处理问题。用户之前询问了如何将两个事务提交改为平行而非嵌套的方式。在之前的回答中,我们使用了嵌套事务(一个using语句嵌套另一个)来处理跨文档操作。现在用户希望改为平行方式,即两个事务独立启动和提交,而不是嵌套。在CAD二次开发中,事务(Transaction)是用来管理数据库操作的基本单位。每个文档都有自己的事务管理器(TransactionManager),每个事务必须在其所属文档的事务管理器中启动和提交(或回滚)。嵌套事务是指在一个事务的作用域内开启另一个事务。而平行事务(或称为独立事务)是指两个事务没有嵌套关系,各自独立。但是,请注意,在跨文档操作中,我们通常需要确保两个文档的操作要么都成功,要么都失败(即保持原子性)。嵌套事务通过将两个事务放在同一个try-catch块中,可以方便地实现这一点:如果一个事务失败,我们可以同时回滚两个事务。如果改为平行事务,即分别启动两个事务,然后分别提交,那么就需要更复杂的错误处理来保证原子性。例如,如果第一个事务提交成功,但第二个事务提交失败,那么第一个事务已经提交,无法回滚,这就会导致数据不一致。不过,如果用户确实需要平行事务(非嵌套)的方式,我们可以分别启动两个事务,并分别进行提交。但我们必须注意,这种方式无法保证原子性。以下是平行事务的示例代码:步骤:1.分别启动两个文档的事务(不嵌套)。2.分别进行两个文档的操作。3.分别提交两个事务,但需要特别注意错误处理。但是,由于两个事务独立,我们无法在提交第一个事务后,如果第二个事务失败而回滚第一个事务(因为第一个事务已经提交)。所以,这种模式通常用于两个操作相对独立,且可以接受部分成功的情况。如果我们希望保持原子性,那么我们需要在第一个事务提交前确保第二个事务的操作不会失败。或者,我们可以采用补偿机制(即如果第二个事务失败,则尝试撤销第一个事务的操作),但在CAD中,撤销操作通常是通过事务回滚来实现的,一旦提交就无法撤销。因此,在要求原子性的场景下,平行事务并不合适。然而,根据用户的要求,我们给出平行事务的代码结构:```csharp//假设有两个文档:doc1和doc2Documentdoc1=...;Documentdoc2=...;Transactiontr1=null;Transactiontr2=null;try{//启动第一个事务tr1=doc1.TransactionManager.StartTransaction();//在doc1中执行操作//...操作代码//启动第二个事务(与第一个事务平行)tr2=doc2.TransactionManager.StartTransaction();//在doc2中执行操作//...操作代码//分别提交两个事务tr1.Commit();tr2.Commit();}catch(Exceptionex){//如果发生错误,回滚所有事务if(tr1!=null&&tr1.IsActive)tr1.Abort();if(tr2!=null&&tr2.IsActive)tr2.Abort();//处理异常}finally{//释放事务资源if(tr1!=null)tr1.Dispose();if(tr2!=null)tr2.Dispose();}```注意:上面的代码中,两个事务是独立启动的,但是它们是在同一个try块中。提交时先提交tr1,再提交tr2。如果tr1提交成功,但tr2提交失败(比如在tr2.Commit()时抛出异常),那么tr2会被回滚,但tr1已经提交,无法回滚。因此,这不是原子操作。如果我们希望两个事务要么都提交,要么都不提交,那么我们必须确保在提交tr1之前,tr2的操作不会在提交时失败。但是,由于事务的提交是独立的,我们无法预知tr2的提交是否会失败。因此,在需要原子性的跨文档操作中,我们仍然推荐使用嵌套事务(即在一个using作用域内启动两个事务,然后一起提交)。嵌套事务的结构更清晰,且可以确保在发生错误时同时回滚。但是,如果用户确实需要平行事务,并且可以接受非原子性,那么上面的代码结构是可以的。另外,还有一种方式:我们可以将两个事务的启动都放在try块中,然后先执行两个事务中的所有操作(但不提交),最后再按顺序提交。这样,如果任何一个操作发生错误(在提交之前),都可以回滚两个事务。但是,提交操作本身也可能失败(例如,由于数据库约束、磁盘空间不足等),所以提交阶段的错误仍然会导致部分提交。总结:在CAD二次开发中,跨文档的平行事务处理需要谨慎,因为无法保证原子性。在大多数情况下,嵌套事务(在一个作用域内)是更好的选择。如果用户有特殊原因必须使用平行事务(例如,避免长时间锁定资源等),那么可以采用上述结构,但要注意其风险。现在,根据用户的要求,我们给出一个具体的跨文档复制示例,使用平行事务:```csharp[CommandMethod("ParallelCopy")]publicstaticvoidParallelCopySelectedEntity(){DocumentCollectiondocColl=Application.DocumentManager;DocumentactiveDoc=docColl.MdiActiveDocument;Editored=activeDoc.Editor;//选择要复制的对象PromptEntityResultper=ed.GetEntity("\n选择要复制的对象:");if(per.Status!=PromptStatus.OK)return;//选择目标文档PromptIntegerResultpir=ed.GetInteger("\n输入目标文档索引(0-based):");if(pir.Status!=PromptStatus.OK||pir.Value>=docColl.Count)return;DocumenttargetDoc=docColl[pir.Value];//定义两个事务TransactionsrcTrans=null;TransactiontrgTrans=null;try{//启动源文档事务srcTrans=activeDoc.TransactionManager.StartTransaction();//获取源对象EntitysrcObj=srcTrans.GetObject(per.ObjectId,OpenMode.ForRead)asEntity;//启动目标文档事务(平行事务)trgTrans=targetDoc.TransactionManager.StartTransaction();//深拷贝到目标数据库EntityclonedObj=(Entity)srcObj.DeepClone(targetDoc.Database,true);//添加到目标模型空间BlockTablebt=(BlockTable)trgTrans.GetObject(targetDoc.Database.BlockTableId,OpenMode.ForRead);BlockTableRecordbtr=(BlockTableRecord)trgTrans.GetObject(bt[BlockTableRecord.ModelSpace],OpenMode.ForWrite);btr.AppendEntity(clonedObj);trgTrans.AddNewlyCreatedDBObject(clonedObj,true);//先提交源文档事务?实际上,源文档我们只是读取,没有修改,所以提交或不提交都没有改变。//但是为了事务的完整性,我们按照顺序提交。//注意:这里源文档没有写操作,所以提交不会改变任何东西,但为了规范,我们还是提交。srcTrans.Commit();trgTrans.Commit();ed.WriteMessage($"\n成功复制到文档:{targetDoc.Name}");}catch(Exceptionex){ed.WriteMessage($"\n错误:{ex.Message}");//回滚事务if(srcTrans!=null&&srcTrans.IsActive)srcTrans.Abort();if(trgTrans!=null&&trgTrans.IsActive)trgTrans.Abort();}finally{//释放事务if(srcTrans!=null)srcTrans.Dispose();if(trgTrans!=null)trgTrans.Dispose();}}```在上面的代码中,我们分别启动了两个事务,然后分别提交。注意,源文档事务(srcTrans)只是用于读取对象,所以即使提交,也没有实际的数据修改。而目标文档事务(trgTrans)用于写入,所以提交是必要的。但是,如果目标文档事务提交失败,那么源文档事务已经提交(虽然它没有做任何修改,所以无所谓),而目标文档事务会回滚。因此,在这个特定场景下,由于源文档没有修改,所以不会造成不一致。但是,如果我们需要在两个文档中都进行修改,那么使用平行事务就需要谨慎了。因此,我们建议:-如果两个操作都是只读的,那么平行事务没有问题。-如果两个操作都是写操作,且需要原子性,则使用嵌套事务(在一个using作用域内同时管理两个事务,并一起提交)。-如果两个操作都是写操作,但可以接受非原子性,则可以使用平行事务。最后,请注意,CAD的事务是数据库事务,每个文档的数据库是独立的,所以无法实现跨文档的分布式事务(两阶段提交)。因此,在需要强一致性的场景下,应尽量避免跨文档的写操作。希望这个回答能够帮助用户理解如何实现平行事务,以及其局限性。生成相关问题:</think>### C# CAD 二次开发:事务平行处理(非嵌套)实现方案 在 CAD 二次开发中,实现事务的平行处理(非嵌套)需要采用**独立事务作用域**和**显式事务管理**策略。以下是关键实现方法: #### 一、核心实现原理 1. **独立事务作用域** 每个事务使用独立的 `using` 作用域,避免嵌套: ```csharp using (Transaction tr1 = doc1.TransactionManager.StartTransaction()) { // 文档1操作 tr1.Commit(); } using (Transaction tr2 = doc2.TransactionManager.StartTransaction()) { // 文档2操作 tr2.Commit(); } ``` 2. **事务状态显式管理** 手动控制事务提交/回滚: ```csharp Transaction tr1 = doc1.TransactionManager.StartTransaction(); Transaction tr2 = doc2.TransactionManager.StartTransaction(); try { // 操作代码... tr1.Commit(); tr2.Commit(); } catch { tr1.Abort(); tr2.Abort(); } finally { tr1.Dispose(); tr2.Dispose(); } ``` #### 二、完整平行事务处理示例 ```csharp using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; public class ParallelTransactions { [CommandMethod("ParallelEdit")] public static void ExecuteParallelTransaction() { DocumentCollection docColl = Application.DocumentManager; Document doc1 = docColl.MdiActiveDocument; Document doc2 = docColl[0]; // 获取第一个文档 // 独立事务作用域 - 方式1 using (Transaction tr1 = doc1.TransactionManager.StartTransaction()) using (Transaction tr2 = doc2.TransactionManager.StartTransaction()) { // 文档1操作 BlockTable bt1 = tr1.GetObject(doc1.Database.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr1 = tr1.GetObject(bt1[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // 文档2操作 BlockTable bt2 = tr2.GetObject(doc2.Database.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr2 = tr2.GetObject(bt2[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // 创建新对象(示例) Circle circle1 = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 5); Circle circle2 = new Circle(new Point3d(10, 10, 0), Vector3d.ZAxis, 3); btr1.AppendEntity(circle1); btr2.AppendEntity(circle2); tr1.AddNewlyCreatedDBObject(circle1, true); tr2.AddNewlyCreatedDBObject(circle2, true); // 平行提交事务 tr1.Commit(); tr2.Commit(); } // 显式事务管理 - 方式2 Transaction trA = null; Transaction trB = null; try { trA = doc1.TransactionManager.StartTransaction(); trB = doc2.TransactionManager.StartTransaction(); // 跨文档操作代码... trA.Commit(); trB.Commit(); } catch (Exception ex) { doc1.Editor.WriteMessage($"错误: {ex.Message}"); if (trA != null && trA.IsActive) trA.Abort(); if (trB != null && trB.IsActive) trB.Abort(); } finally { trA?.Dispose(); trB?.Dispose(); } } } ``` #### 三、关键注意事项 1. **原子性保证** 使用 `try-catch-finally` 确保异常时所有事务回滚: ```csharp try { /* 操作 */ } catch { tr1.Abort(); // 回滚事务1 tr2.Abort(); // 回滚事务2 } ``` 2. **性能优化** 批量操作时合并操作减少提交次数: ```csharp // 单次提交所有变更 tr1.Commit(); tr2.Commit(); ``` 3. **资源释放** 必须显式释放事务资源: ```csharp finally { tr1?.Dispose(); // 确保资源释放 tr2?.Dispose(); } ``` 4. **文档锁定** 跨文档操作时锁定文档防止冲突: ```csharp using (doc1.LockDocument()) using (doc2.LockDocument()) { // 安全操作区域 } ``` #### 四、应用场景 1. 同时更新多个图纸的标题栏信息 2. 批量同步多个文档的图层设置 3. 跨文档参数化设计变更 4. 分布式计算任务处理 > **提示**:对于复杂对象复制,仍需使用 `DeepClone` 方法处理对象依赖关系[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值