jfinal 事务回滚问题

zhoumaomao 发布于 2014/01/13 16:18
阅读 9K+
收藏 2

@JFinal 你好,想跟你请教个问题:在学习和使用jfinal 框架,写了一个类TestService 想做中间层。里面有两个 model test 和test2 。 在TestService中分别new 出两个model 然后做数据库插入操作,在test 插入操作之后抛出sqlException("出错")  在类中用的是@Brfore(Tx.class)的注解。 不知为何不会回滚

代码如下:


package com.woniu157.sys.test;

import java.sql.Connection;

import com.jfinal.aop.Before;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.tx.Tx;
import com.woniu157.sys.user.User;

public class TestService {
	@Before(Tx.class)
	public void testTran() throws Exception{
		//DbKit.setThreadLocalConnection(null);
		Test t=new Test();
		Test2 t2=new Test2();
		t2.getsomeThine();
		t.getsomeThine();
	}
	
	

}


package com.woniu157.sys.test;

import java.sql.SQLException;

import com.jfinal.plugin.activerecord.Model;

public class Test2 extends Model<Test2> {
	public static final Test2 dao=new Test2();
	
	public void getsomeThine() throws SQLException{
		dao.set("id", 1);
		dao.set("name", "zm33m");
		dao.save();
		throw new SQLException("");
	}
}


package com.woniu157.sys.test;

import com.jfinal.plugin.activerecord.Model;

public class Test extends Model<Test> {
	public static final Test dao=new Test();
	
	public void getsomeThine(){
		dao.set("id", 1);
		dao.set("name", "zmm");
		dao.save();
	}
}

加载中
1
JFinal
JFinal

    建议仔细看一下 jfinal 手册,一共才30 多页,而且大部分是代码示例。楼主碰到的问题在jfinal 手册第25页有如下说明:


0
z
zhoumaomao

引用来自“JFinal”的答案

    建议仔细看一下 jfinal 手册,一共才30 多页,而且大部分是代码示例。楼主碰到的问题在jfinal 手册第25页有如下说明:


我还有个三个问题,还望赐教

问题1 :即使我把上面保存方法换成new Model.Set(...),在最后抛出错误 ,数据库依然能够保存成功。

@Before(Tx.class)
public void doSomeThine(){

  Test t=new Test();
  t.set("id",1111);
  t.set("name","zhoumaomao");
  t.save();
  Test2 t=new Test2();
  t2.set("id",33333);
  t2.set("name","zmm");
  t2.save();
  throw new sqlException("出错啦...");
}
问题二:如果我改用db.tx(obj instance IAtom) 就能够原子执行,我看您的源码都是从线程中获取conn 。不知为何

问题三:我看您的@Before(Tx.class)都写在Action 层,那这样 假设我手动抛出异常,在Action层如果手动捕捉,那Tx 就捕捉不到,如果再往上抛出,那则遇到“系统错误”请问这种情况该怎么处理?

0
h
huson

引用来自“zhoumaomao”的答案

引用来自“JFinal”的答案

    建议仔细看一下 jfinal 手册,一共才30 多页,而且大部分是代码示例。楼主碰到的问题在jfinal 手册第25页有如下说明:


我还有个三个问题,还望赐教

问题1 :即使我把上面保存方法换成new Model.Set(...),在最后抛出错误 ,数据库依然能够保存成功。

@Before(Tx.class)
public void doSomeThine(){

  Test t=new Test();
  t.set("id",1111);
  t.set("name","zhoumaomao");
  t.save();
  Test2 t=new Test2();
  t2.set("id",33333);
  t2.set("name","zmm");
  t2.save();
  throw new sqlException("出错啦...");
}
问题二:如果我改用db.tx(obj instance IAtom) 就能够原子执行,我看您的源码都是从线程中获取conn 。不知为何

问题三:我看您的@Before(Tx.class)都写在Action 层,那这样 假设我手动抛出异常,在Action层如果手动捕捉,那Tx 就捕捉不到,如果再往上抛出,那则遇到“系统错误”请问这种情况该怎么处理?

try {
conn = DbKit.getConnection();
autoCommit = conn.getAutoCommit();
DbKit.setThreadLocalConnection(conn);
conn.setTransactionIsolation(getTransactionLevel()); // conn.setTransactionIsolation(transactionLevel);
conn.setAutoCommit(false);
invocation.invoke();
conn.commit();
} catch (Exception e) {
if (conn != null)
try {conn.rollback();} catch (Exception e1) {e1.printStackTrace();}
throw new ActiveRecordException(e);

}

的确,事务的回滚在拦截器那边,如果action层捕获异常,则事务无法回滚了..在该怎么解决?

0
JFinal
JFinal

引用来自“huson”的答案

引用来自“zhoumaomao”的答案

引用来自“JFinal”的答案

    建议仔细看一下 jfinal 手册,一共才30 多页,而且大部分是代码示例。楼主碰到的问题在jfinal 手册第25页有如下说明:


我还有个三个问题,还望赐教

问题1 :即使我把上面保存方法换成new Model.Set(...),在最后抛出错误 ,数据库依然能够保存成功。

@Before(Tx.class)
public void doSomeThine(){

  Test t=new Test();
  t.set("id",1111);
  t.set("name","zhoumaomao");
  t.save();
  Test2 t=new Test2();
  t2.set("id",33333);
  t2.set("name","zmm");
  t2.save();
  throw new sqlException("出错啦...");
}
问题二:如果我改用db.tx(obj instance IAtom) 就能够原子执行,我看您的源码都是从线程中获取conn 。不知为何

问题三:我看您的@Before(Tx.class)都写在Action 层,那这样 假设我手动抛出异常,在Action层如果手动捕捉,那Tx 就捕捉不到,如果再往上抛出,那则遇到“系统错误”请问这种情况该怎么处理?

try {
conn = DbKit.getConnection();
autoCommit = conn.getAutoCommit();
DbKit.setThreadLocalConnection(conn);
conn.setTransactionIsolation(getTransactionLevel()); // conn.setTransactionIsolation(transactionLevel);
conn.setAutoCommit(false);
invocation.invoke();
conn.commit();
} catch (Exception e) {
if (conn != null)
try {conn.rollback();} catch (Exception e1) {e1.printStackTrace();}
throw new ActiveRecordException(e);

}

的确,事务的回滚在拦截器那边,如果action层捕获异常,则事务无法回滚了..在该怎么解决?

     对于 web 项目来说通常让异常抛出,跳到 500 页面即可,如果还干预更多,可以 Tx 拦截器前面再设置个拦截器在更外层捕获到异常再做后续处理。之所以是异常理应很少出现,意外出现跳去 error 500 并做了日志,已经比较合理

    另外还可以使用boolean result = Db.tx(...) 方法来得到事务是否成功的结果,也可以在外层捕获异常再继续做处理

0
JFinal
JFinal

引用来自“zhoumaomao”的答案

引用来自“JFinal”的答案

    建议仔细看一下 jfinal 手册,一共才30 多页,而且大部分是代码示例。楼主碰到的问题在jfinal 手册第25页有如下说明:


我还有个三个问题,还望赐教

问题1 :即使我把上面保存方法换成new Model.Set(...),在最后抛出错误 ,数据库依然能够保存成功。

@Before(Tx.class)
public void doSomeThine(){

  Test t=new Test();
  t.set("id",1111);
  t.set("name","zhoumaomao");
  t.save();
  Test2 t=new Test2();
  t2.set("id",33333);
  t2.set("name","zmm");
  t2.save();
  throw new sqlException("出错啦...");
}
问题二:如果我改用db.tx(obj instance IAtom) 就能够原子执行,我看您的源码都是从线程中获取conn 。不知为何

问题三:我看您的@Before(Tx.class)都写在Action 层,那这样 假设我手动抛出异常,在Action层如果手动捕捉,那Tx 就捕捉不到,如果再往上抛出,那则遇到“系统错误”请问这种情况该怎么处理?

    问题一:Tx 拦截器只要捕获到异常就一定会回滚事务,检查一下数据库引是否为 innodb(对于mysql来说),myisam不支持事务

    问题二:从 ThreadLocal 中获取 conn 是为了实现事务控制,当ThreadLocal中能取到conn,证明是在事务之中,证明本线之前有数据库操作并获取过 conn,那么本线程当前的数据库操作就不必再从 DataSource中获取 conn,使用本线程已有 conn , 好让多次数据库操作使用同一个conn,从而可以实现统一 commit()

    问题三:在 Tx 拦截器再放置一个拦截器再次捕获 Tx 的往上抛出的异常。也可以用一个独立的拦截器使用 Db.tx(...) 来包裹 ai.invoke(),实现事务,并在 Db.tx(...)捕获异常

0
zeroman1212
zeroman1212
不知道楼主用的是那个版本,在Tx.class里面有个NestedTransactionHelpException,可以在自己的异常处理中中catch抛出这个异常,这样可以自己处理异常并可以回滚数据。
0
天王盖地虎626
天王盖地虎626

引用来自“zeroman1212”的评论

不知道楼主用的是那个版本,在Tx.class里面有个NestedTransactionHelpException,可以在自己的异常处理中中catch抛出这个异常,这样可以自己处理异常并可以回滚数据。
真的吗?我用的是1.8,感觉不行呀
返回顶部
顶部