在业务层加了Duang以后,能在业务层就直接回滚事务吗?

lcg0211 发布于 2016/09/01 13:04
阅读 334
收藏 1

@JFinal 你好,想跟你请教个问题:

JFinal一般的方式是 在控制层使用Duang,然后在控制层try,能完成事务回滚

我现在业务层加了Duang,能在业务层就直接回滚事务吗?代码如下,我测试了一下,并没有回滚,你那边有什么好的解决方案吗?

public class TestTxService {
public final static TestTxService me= Duang.duang(TestTxService.class);
@Before(Tx.class)
public String entry(String str) {
try {
me.addA(); //想在这里就回滚,然而实际情况并没有回滚
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "success";
}

@Before(Tx.class)
public String addA() throws Exception {
TestA a= new TestA();
a.setName("000");
a.save();
addB(null);
return a.getName();

}

public boolean addB(String str) throws Exception {
TestB b= new TestB();
b.setName("t0t");
b.save();
TestB c= new TestB();
//if(str==null)
// throw new Exception("异常了");
c.setName("tt0000000666666"); //这个字段长度太长,报MySQL异常,整个事务需要回滚
return c.save();
}


}

加载中
0
JFinal
JFinal

     代码基本正确,只需要在 catch 块之中,向外抛出异常就可以回滚了,不能吃掉这个异常,因为 Tx 拦截器就是通过捕捉异常来回滚的。

lcg0211
lcg0211
多谢解答
0
lcg0211
lcg0211

引用来自“JFinal”的评论

     代码基本正确,只需要在 catch 块之中,向外抛出异常就可以回滚了,不能吃掉这个异常,因为 Tx 拦截器就是通过捕捉异常来回滚的。

刚才又测试了一下,业务层再抛一次异常给 控制层,事务确实回滚了,但是也发现了一个问题,如果 业务层方法既要抛异常,又有返回值的时候,事务就依然不会回滚,代码如下:
public class TestTxService {
public final static TestTxService me= Duang.duang(TestTxService.class);
@SuppressWarnings("finally")
@Before(Tx.class)
public String entry(String str) throws Exception {
String s=null;
try {
me.addA(); //想在这里就回滚,然而实际情况并没有回滚
s="success";
} catch (Exception e) {
//System.out.println("service异常:"+e.getMessage());
s="service异常:"+e.getMessage();
throw e;
} finally {
return s; //finally这里如果加了return,事务依然不会回滚
}
}

@Before(Tx.class)
public String addA() throws Exception {
TestA a= new TestA();
a.setName("bn000");
a.save();
addB(null);
return a.getName();

}

public boolean addB(String str) throws Exception {
TestB b= new TestB();
b.setName("t0t");
b.save();
TestB c= new TestB();
//if(str==null)
// throw new Exception("异常了");
c.setName("tt0000000666666"); //这个字段长度太长,报MySQL异常
return c.save();
}


}

0
JFinal
JFinal

引用来自“JFinal”的评论

     代码基本正确,只需要在 catch 块之中,向外抛出异常就可以回滚了,不能吃掉这个异常,因为 Tx 拦截器就是通过捕捉异常来回滚的。

刚才又测试了一下,业务层再抛一次异常给 控制层,事务确实回滚了,但是也发现了一个问题,如果 业务层方法既要抛异常,又有返回值的时候,事务就依然不会回滚,代码如下:
public class TestTxService {
public final static TestTxService me= Duang.duang(TestTxService.class);
@SuppressWarnings("finally")
@Before(Tx.class)
public String entry(String str) throws Exception {
String s=null;
try {
me.addA(); //想在这里就回滚,然而实际情况并没有回滚
s="success";
} catch (Exception e) {
//System.out.println("service异常:"+e.getMessage());
s="service异常:"+e.getMessage();
throw e;
} finally {
return s; //finally这里如果加了return,事务依然不会回滚
}
}

@Before(Tx.class)
public String addA() throws Exception {
TestA a= new TestA();
a.setName("bn000");
a.save();
addB(null);
return a.getName();

}

public boolean addB(String str) throws Exception {
TestB b= new TestB();
b.setName("t0t");
b.save();
TestB c= new TestB();
//if(str==null)
// throw new Exception("异常了");
c.setName("tt0000000666666"); //这个字段长度太长,报MySQL异常
return c.save();
}


}

引用来自“lcg0211”的评论

有多种解决方案,按需要随便选一个:

方案一:将 try catch 挪到controller 中

方案二:在业务层用 Db.tx(....) 代替 @Before(Tx.class) 可以更加灵活的处理事务,可以在 tx(...) 方法中返回 false 来实现回滚事务

方案三:在你现有代码的基础上,在 catch 块中使用如下的代码手动回滚事务:

DbKit.getConfig().getConnection().rollback();

   我个人最喜欢方案二,在业务层只要用到事务,全用的方案二,如果觉得代码不好看,可以将具体的业务方面一个单独的方法中,代码大致如下:

public void service(...) {
   boolean isOk = Db.tx(new IAtom() {
     public boolean run() {
       Xxx ret = doService(...);
       return true; // 这里也可以根据具体情况返回 boolean控制事务回滚
     }
   }
}
// 具体的业务放在这里
private Xxx doService(...) {
   
}

   其实熟悉动态语言开发的人会非常喜欢方案二这种函数式编程方法。

返回顶部
顶部