3

I was trying to implement my own method interceptors using annotations (Guice) in Play!. However, it seems like those annotations would only work (and therefore intercept) if the containing classes are created by Guice (reference). That brings me to the question: How does @Transactional work outside of Controller classes in Play!? It is essentially a method interceptor, and it works fine no matter how the containing classes were created? I can use it within my models and service classes as well.

Community
  • 1
  • 1
Saksham Gupta
  • 223
  • 1
  • 2
  • 8
  • Unfortunately @Transactional works only with Controller actions. Any other method you have to wrap manually. This question might help: http://stackoverflow.com/questions/32443032/where-to-place-transactional-annotation-in-play. – Kris Sep 15 '15 at 11:33
  • Hmm, this is quite contrary to what I've seen. If I call a service routine annotated with `@Transaction`, it still seems to work. Does that mean a callee in a call stack originating from anywhere in the controller can get annotated with `@Transaction`, but any other callee, outside the controller context cannot? – Saksham Gupta Sep 15 '15 at 12:22
  • If you call the service method from within an with @Transactional annotated action within an Controller (the whole Controller might be annotated too), then it will work. So, yes if you call the same service method from a different location, outside of your annotated action it won't work. In the docs https://www.playframework.com/documentation/2.4.x/JavaJPA#Annotating-JPA-actions-with-@Transactional they also mention only actions. It's quite a shame: Transactional annotation in arbitrary methods would be quite a feature (it's no problem in the Spring framework). – Kris Sep 15 '15 at 14:28
  • Actually, the fact that Play! does not allow arbitrary methods to respect `@Transaction` makes sense. Play! uses Guice, and with Guice, you cannot invoke annotations based method interceptors unless the object that contains the annotation is created by `injector.getInstance()` call. Play! can control controller object initialization, but not arbitrary object creation. – Saksham Gupta Sep 15 '15 at 18:21

1 Answers1

3

@Transactional doesn't work outside a controller. Your only way is to use JPA.withTransaction

Example:

public Promise<Integer> doWork() {
    return promise(() -> jpaApi.withTransaction(() -> {
        return JPA.em()
            .createNativeQuery("DELETE FROM table WHERE id=1")
            .executeUpdate();
    }), dbExecutionContext);
}

Or even without additional execution context (executes in the caller thread):

public Promise<Integer> doWork() {
    return jpaApi.withTransaction(() -> {
        return JPA.em()
                .createNativeQuery("DELETE FROM table WHERE id=1")
                .executeUpdate();
    });
}

Don't forget to inject play.db.jpa.JPAApi.

dzagorovsky
  • 911
  • 8
  • 11