用Lambda武裝你的Java: 靈活的事務

當你採用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是能夠的。事務

相關文章
相關標籤/搜索