Reputation: 177
I have started to use Guice method-level transactions like described here. I have a message like
@Inject
private EntityManager entityManager;
@Transactional
public UserSession createSession(User user, String browser) {
UserSession session = new UserSession(user, browser);
entityManager.persist(session);
}
From the short description i thought wis should be enough. But i get an error cause no transaction is started. It works only if i start and commit it by myself.
The Object is created by Guice on the Start of my Application in an initializer. the same Instance is used for each request.
Why is it not working?
Upvotes: 9
Views: 6299
Reputation: 2638
In addition to other answers, particularly Jeff Bowman and uldall's:
I just ran into this problem because I was using a Java 17 runtime with a version of Guice (5.0.1) that doesn't support it; it requires at least 5.1.0!
You may need to debug your CLASSPATH. If you're using a dependency manager:
:dependencies
task will help debug that.implementation("another-dependency:x.y.z") {
exclude group: 'com.google.inject', module: 'guice'
}
It's a little annoying that there is no feedback when Guice fails to properly instrument the code, beside a subsequent exception being thrown (eg javax.persistence.TransactionRequiredException
).
In order to verify that it's actually working, it may be useful to inspect a call stack within a @Transactional
method, as in:
@Transactional
void myTransactionalMethod() {
StackWalker.getInstance().forEach(System.out::println);
// rest of method
}
If Guice's instrumentation is working correctly, you'll find com.google.inject.persist.jpa.JpaLocalTxnInterceptor
in the call stack on stdout.
You can also use your IDE's debugger; simply put a breakpoint at the beginning of the @Transactional
method and inspect the call stack.
This allows verifying that even as of the current version of Guice (5.1.0), Guice still doesn't support the javax.transaction.Transactional
annotation, per uldall's answer.
Upvotes: 0
Reputation: 2558
I had a problem similar to yours and it was resolved by switching from @javax.transaction.Transactional to @com.google.inject.persist.Transactional. Apparently Guice-Persist doesn't support the @Transactional annotation from the Java Transaction API.
Upvotes: 3
Reputation: 2429
I had this problem in the following situation, so I thought I'd post my solution here, too:
BusinessLogic
requires two constructor arguments: MyDao
, which I could theoretically get from guice, and some other object that I could not get from guice.
So I created a BusinessLogicProvider
(extends AbstactProvider
), but it's not used with bind(BusinessLogic.class).toProvider(BusinessLogicProvider)
).
Now I just bind the BusinessLogicProvider
like any guice-served type: bind(BusinessLogicProvider.class);
Inside my BusinessLogicProvider
class I can now use @Inject
on a private Provider<MyDao> daoProvider;
Later, in the BusinessLogicProvider
's public BusinessLogic get()
method, I can then call BusinessLogic
's constructor with the two arguments required: daoProvider.get()
and the other object I cannot get from guice.
The Pitfall: when the @Inject
ed private Provider<MyDao> daoProvider;
of my BusinessLogicProvider
is not of type Provider<MyDao>
(but simpy of type MyDao
), it will not work.
Even if the @Inject
ed MyDao
would have come from guice, guice needs to create a new one every time you instantiate BusinessLogic
.
Upvotes: 0
Reputation: 95654
@Transactional
method annotations work through AOP
, in which Guice fulfills a request for Foo
by creating a proxy object that intercepts those annotated method calls and (optionally) forwards them to the actual object. Make sure that the following are true:
You have created the object with the @Transactional
method through Guice, since Guice otherwise won't have any chance to provide the proxy instead.
Neither the class nor the method is marked final
, since AOP can't override those easily.
You have installed JpaPersistModule, or some other form of PersistModule. Note from that source code that this module is actually what binds the MethodInterceptor
to the @Transactional
annotation.
If this doesn't fit your needs exactly, remember that you can always go with the AOP documentation to write your own method interceptor. Good luck!
Upvotes: 7
Reputation: 177
After checking everything again it did not work. The funny part is that it worked in between, but only every hundred times.
After some additional testing i found that i need to recreate the Classes on each Request. Before i just created them at application start. Now it seems to work perfectly.
Thanks for tips helped me to investigate it further.
Upvotes: 2