用Lambda武装你的Java: 灵活的事务

原文发表于2015-04-12。

当你采用Spring之类的框架,用了声明式事务,难道每一段需要事务的代码都必须写成一个bean method,再标上@Transactional?

未免太麻烦了,不信你瞧。假如你写了类似这样的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);虽然不需要事务,却也被包在事务里了。

我们可以做得更好!用Java 8做一个Transactor,任何代码块可随时包在事务中!
然后controller可以重写为:

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 apply(Supplier 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;
}

代码中透出四个字:灵活,简洁!

2015/7/5 Update: 可惜的是,有时会抛出org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread (虽然stacktrace中是有代理类的)
目前实测改用Spring的TransactionTemplate是可以的。

posted @ 2020-12-25 17:27  计算法  阅读(90)  评论(0)    收藏  举报