当你采用Spring之类的框架,用了声明式事务,难道每一段须要事务的代码都必须写成一个bean method,再标上@Transactional?app
未免太麻烦了,不信你瞧。假如你写了相似这样的Controller和Service (伪代码):框架
class UserController { @Autowired UserService us; String updateUser(long userId) { User user = us.authorize(userId); Event e = us.update(user); publishToMQ(e); return "user-page"; } } @Transactional class UserService { User authorize(long userId) {...} void update(User user) {...} }
问题来了,authorize和update分别用了两个分开的事务,若是你用了Hibernate或JPA,而且user是lazy-loading的,这就无法运行。你须要让两次调用运行在同一个事务里。一般的办法是把UserController.updateUser也标成@Transactional。但是这么一来,下一句publishToMQ(e);
虽然不须要事务,却也被包在事务里了。this
咱们能够作得更好!用Java 8作一个Transactor,任何代码块可随时包在事务中!
而后controller能够重写为:hibernate
String updateUser(long userId) { Event e = Transactor.get().apply(() -> { User user = us.authorize(userId); return us.update(user); }); publishToMQ(e); return "user-page"; }
Transactor的实现:代理
@Component @Transactional public class Transactor { public static Transactor get() { return instance; } public <R> R apply(Supplier<R> f) { return f.get(); // 有返回值的代码块 } public void run(Runnable f) { f.run(); // 无返回值的代码块 } @Autowired private ApplicationContext applicationContext; @PostConstruct void setup() { instance = applicationContext.getBean(Transactor.class); //不能写instance=this } private static Transactor instance; }
代码中透出四个字:灵活,简洁!code
2015/7/5 Update: 惋惜的是,有时会抛出org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread (虽然stacktrace中是有代理类的)
目前实测改用Spring的TransactionTemplate是能够的。事务