Provider Guice <EntityManager> vs EntityManager
I tried to get a simple webapp working with Guice and JPA on Jetty using the persistence and servlet extensions.
I wrote this service implementation class:
public class PersonServiceImpl implements PersonService { private EntityManager em; @Inject public PersonServiceImpl(EntityManager em) { this.em = em; } @Override @Transactional public void savePerson(Person p) { em.persist(p); } @Override public Person findPerson(long id) { return em.find(Person.class, id); } @Override @Transactional public void deletePerson(Person p) { em.remove(p); } }
And this is my servlet (annotated @Singleton):
@Inject PersonService personService; @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); String password = req.getParameter("password"); String email = req.getParameter("email"); int age = Integer.valueOf(req.getParameter("age")); Person p = new Person(); p.setAge(age); p.setName(name); p.setEmail(email); p.setPassword(password.toCharArray()); logger.info("saving person"); personService.savePerson(p); logger.info("saved person"); logger.info("extracting person"); Person person = personService.findPerson(p.getId()); resp.getWriter().print("Hello " + person.getName()); } When I run this, it works, and I get the name sent to the client, but when I look at the log, I see that the DML generated for insertion and the selection from postgresql does not return any results, which means it was not really persistent.
I was debugging the code and I saw that JpaLocalTxnInterceptor is called txn.commit() .
Then I made changes to PersonServiceImpl and used Provider<EntityManager> instead of EntityManager , and it worked as expected. Now I really don't understand why, and probably because I really don't understand the idea of ββthe provider. The Guice wiki page says:
Please note that if you create MyService @Singleton, then you must enter the provider instead.
However, my PersonServiceImpl is not @Singleton, so I'm not sure why it is being applied, possibly because of the servlet?
I would really appreciate it if you understood this.
You need a Provider<EntityManager> because the built-in persistence and extensions of the Guice servlet expect EntityManager to be a request scope. By entering an EntityManager that spans requests from a service stored on the same servlet, you do an extension with a scale extension, and Guice will not store data from an obsolete, inconsistent EntityManager.
Providers
A provider is a one-method interface that provides the get() method. If you type Provider<Foo> and then call get() , it will return an instance created just as if you directly entered Foo. However, Provider's injection allows you to control how many objects are created and when they are created. This can be useful in several cases:
- only instantiation if really needed, especially if the creation takes a lot of time or memory
- creating two or more separate instances from the same component
- defer creation to initialization method or separate thread
- mixing areas as described below
To bind X , Provider<X> or @Provides X , Guice will automatically allow you to enter either X or Provider<X> directly. You can use Providers without adjusting any bindings, and Providers work fine with annotation binding .
Areas and expanding injections
Generally speaking, scopes determines the lifetime of an object. By default, Guice creates a new object for each injection; by tagging the @Singleton object, you instruct Guice to enter the same instance for each injection. Guice servlet extensions also support @RequestScoped and @SessionScoped injections, which force the same object to be injected in the same request (or session) sequentially, but for a new object that must be entered for another request (or session). Guice also allows you to define custom areas, such as a stream region (one instance for the stream, but the same injection instance in the same stream).
@Singleton public class YourClass { @Inject HttpServletRequest request; // BAD IDEA } What happens if you enter an object with a request scope directly from the @Singleton component? When a singleton is created, it tries to inject an instance related to the current request. Please note that there may not be a current request, but if there is one, the instance will be saved in the field in singleton mode. As requests come and go, the singleton is never recreated, and the field is never reassigned, so after the first request, your component stops working normally.
Embedding an object with a narrow scope (@RequestScoped) into a wide range (@Singleton) is called an extension extension. Not all volume-expanding injections show symptoms immediately, but all can introduce lingering errors later.
How providers help
PersonService is not annotated with @Singleton, but since you insert and save the instance into the @Singleton servlet, it can also be single. This means that the EntityManager also has singleton behavior for the same reasons.
According to the page you specified , EntityManager should be short-lived, existing only for a session or request. This allows Guice to automatically commit the transaction at the end of a session or request, but reusing the same EntityManager will most likely prevent data from being stored at any time after the first. Switching to a provider allows you to limit the scope by creating a new EntityManager with every request.
(You can also make PersonService a provider, which is also likely to solve the problem, but I think it's better to follow Guice's recommendations and keep the EntityManager area clearly narrow with the provider.)