Mohamed Taboubi
Mohamed Taboubi

Reputation: 7011

Commit EntityManager Transaction using @Transactional - Guice

I'm using Guice to Inject EntityManager. When I commit the trasaction of the injected entityManager there is nothing happend in the BD side : no transaction passed !!! can you help me to figure out what is going on ?

Here is my code :

Web.xml

  <filter>
    <filter-name>guiceFilter</filter-name>
    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    <async-supported>true</async-supported>
  </filter>

  <filter-mapping>
    <filter-name>guiceFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <listener>
    <listener-class>ca.products.services.InjectorListener</listener-class>
  </listener>

The InjectorListener class :

public class InjectorListener extends GuiceServletContextListener {
    @Override
    protected Injector getInjector() {

        return Guice.createInjector(
                new PersistenceModule(),
                new GuiceModule(),
                new RestModule());
    }
}

The persistenceModule class :

public class PersistenceModule implements Module {
    @Override
    public void configure(Binder binder) {
        binder
                .install(new JpaPersistModule("manager1")
                        .properties(getPersistenceProperties()));

        binder.bind(PersistenceInitializer.class).asEagerSingleton();
    }

    private static Properties getPersistenceProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.connection.driver_class", "org.postgresql.Driver");
        properties.put("hibernate.connection.url", "jdbc:postgresql://localhost:5432/postgres");
        properties.put("hibernate.connection.username", "postgres");
        properties.put("hibernate.connection.password", "postgres");
        properties.put("hibernate.connection.pool_size", "1");
        properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        properties.put("hibernate.hbm2ddl.auto", "create");

        return properties;
    }
}

The GuiceModule class :

public class GuiceModule extends AbstractModule {

    @Override
    protected void configure() {

        bind(MemberRepository.class).to(MemberRepositoryImp.class);
        bind(ProductRepository.class).to(ProductRepositoryImpl.class);
        bind(ShoppingBagRepository.class).to(ShoppingBagRepositoryImpl.class);
    }
}

The RestModule class :

public class RestModule extends JerseyServletModule {

    @Override
    protected void configureServlets() {

        HashMap<String, String> params = new HashMap<>();
        params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "ca.products.services");
        params.put(JSONConfiguration.FEATURE_POJO_MAPPING, "true");
        params.put(ResourceConfig.FEATURE_DISABLE_WADL, "true");

        serve("/*").with(GuiceContainer.class, params);
    }
} 

and Finally the webservice (jeresy) call:

    @Inject
    private Provider<EntityManager> em;

    @GET
    @Transactional
    @Path("/reset")
    public void resetData() {

        logger.info("Processing reset");
        try {

            em.get().getTransaction().begin();

            for (int i = 0; i < 10; i++) {
                em.get().persist(new Product("Product_" + i, "Desc_" + i));
            }
            em.get().flush();
            em.get().getTransaction().commit();

        } catch (Exception e) {
            throw new WebApplicationException(Response.Status.FORBIDDEN);
        }
    }

Upvotes: 0

Views: 836

Answers (1)

Chris Hinshaw
Chris Hinshaw

Reputation: 7255

You probably need to add the a persist filter. This will also keep you from having to manage transactions manually. If you do not use the filter you can still inject the UnitOfWork to create transactions. If you are using jpa persist you should not be managing userTransactions.

This is a custom filter that also adds a Lifecycle which it automatically started on startup with some custom code and a map binder builder. It is only there for thoroughness. It is not part of the guice api but more similar to spring's Lifecycle listener. I don't have any spring dependencies at all.

@Singleton
public final class JpaPersistFilter implements Filter {

    private final UnitOfWork unitOfWork;
    private final PersistServiceLifecycle persistService;

    @Inject
    public JpaPersistFilter(UnitOfWork unitOfWork, PersistServiceLifecycle persistService) {
        this.unitOfWork = unitOfWork;
        this.persistService = persistService;
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        // persistService.start();
    }

    public void destroy() {
        persistService.stop();
    }

    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
            final FilterChain filterChain) throws IOException, ServletException {

        unitOfWork.begin();
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            unitOfWork.end();
        }
    }

    /**
     * Extra lifecycle handler for starting and stopping the service. This
     * allows us to register a {@link Lifecycle} with the
     * {@link LifecycleListener} and not have to worry about the service being
     * started twice.
     * 
     * @author chinshaw
     *
     */
    @Singleton
    public static class PersistServiceLifecycle implements Lifecycle {

        private final PersistService persistService;

        private volatile boolean isStarted = false;

        @Inject
        public PersistServiceLifecycle(PersistService persistSerivce) {

            this.persistService = persistSerivce;
        }

        @Override
        public boolean isRunning() {
            return isStarted;
        }

        @Override
        public void start() {
            if (!isStarted) {
                persistService.start();
                isStarted = true;
            }
        }

        @Override
        public void stop() {
            persistService.stop();
            isStarted = false;
        }
    }
}

Example of adding filter to module.

@Override
protected void configureServlets() {
    filter("/api/*").through(JpaPersistFilter.class);
}

Example of using unit of work to manage the transaction.

@Inject
UnitOfWork unitOfWork;
public void doSomething() {
    unitOfWork.begin();
    try {
         dao.saveState(someobject);
    } finally {
        unitOfWork.end();
    }
}

Upvotes: 1

Related Questions