示例代码
以下代码的方法addBook(Book book)有意抛出一个数据库相关的运行时异常,调用者registerBook()方法却不能捕获到该异常。
package walker.basewf.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import walker.basewf.dao.BookDao;
import walker.basewf.vo.Book;
@Service
@Transactional
public class TrapAService {
@Autowired
private BookDao bookDao;
@Transactional(rollbackFor = Exception.class)
public void registerBook(Long userId, Book book) {
try {
System.out.println(">>Begin...");
this.addBook(book);
//虽然上行代码有运行时异常,但似乎并没有及时抛出,所以仍然打印出">>Success!"
System.out.println(">>Success!");
} catch (Exception e) {
//没有捕获到异常,这行代码不会被执行!奇怪!
System.out.println(">>Failed, here catched a exception! ");
e.printStackTrace();
}
}
@Transactional(rollbackFor = Exception.class)
public void addBook(Book book) throws Exception {
//往Book表插入一条记录,有意制造主键冲突或列名不正确的运行时异常
bookDao.save(book);
}
}
package walker.basewf.service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import walker.basewf.common.utils.DateTimeUtil;
import walker.basewf.vo.Book;
import java.util.Date;
@Service
@Transactional
public class Trap3Test {
public static ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
public static void main(String[] args) {
Book book = new Book();
book.setTitle(new String("三国演义"));
book.setPrice(new Double(200.0));
book.setPublishTime(DateTimeUtil.toSqlDate(new Date()));
try {
TrapAService aService = context.getBean(TrapAService .class);
aService.registerBook(new Long(101), book);
} catch (Exception e) {
e.printStackTrace();
}
}
}
其它讨论
对于以上现象,我困惑不已,很多网友也遇到这个问题,关于这个问题的讨论见:
http://bbs.csdn.net/topics/390050093?page=1#post-398522654
http://bbs.csdn.net/topics/390688795
http://www.360doc.com/content/12/1109/18/6161903_246870991.shtml
结论(2015-10-13)
后来,发现这个问题仅与myBatis有关:由于我的mybatis.xml中的<configuration><settings>内有一行: <setting name="defaultExecutorType" value="BATCH"/>
这样就使得mybatis在执行dao.save()动作时,只是执行了相当于JDBC的stmt.addBatch(),而不是stmt.execute(), 所以此时不会抛出主键冲突等运行时异常;而只有等到代理对象在临近 commit前执行stmt.execteBatch() 后才会抛出异常。
我将参数"defaultExecutorType"的值由"BATCH"改为:"REUSE"或"SIMPLE",就解决了这个问题。