2 Replies Latest reply on Dec 22, 2009 3:39 PM by balazska

    Threadsafe persist in multithread environment?

    hurzeler

      I have a simple multithreaded action on JBoss 4.2.3, JRE 1.6.0_02, Seam 2.1 GA where I would like to harness the database connection pool of the container for performance reasons.


      However an exception is thrown at the persist method call. I suspect this is because the EntityManager is not threadsafe.


      How would I persist in a threadsafe way?


      package com.risk.session;
      
      import java.io.IOException;
      import java.util.Calendar;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      
      import org.jboss.seam.ScopeType;
      import org.jboss.seam.annotations.Logger;
      import org.jboss.seam.annotations.Name;
      import org.jboss.seam.annotations.Scope;
      import org.jboss.seam.annotations.Transactional;
      import org.jboss.seam.framework.EntityController;
      import org.jboss.seam.log.Log;
      
      import com.risk.entity.TestEvent;
      
      @Name("processor")
      public class Processor extends EntityController
      {
      
          /**
           * 
           */
          private static final long serialVersionUID = -8691085937149866076L;
      
          @Logger
          Log log;
      
          @Transactional
          public void process() throws IOException
          {
           log.info("Processing...");
      
           // Spawn threads to optimise the process
           ExecutorService threadExecutor = Executors.newFixedThreadPool(2);
           for (int i = 0; i < 2; i++)
           {
               threadExecutor.execute(new ProcessThread(i));
           }
          }
      
          class ProcessThread implements Runnable
          {
      
           private int threadNo;
      
           public ProcessThread(int threadNo)
           {
               this.threadNo = threadNo;
           }
      
           // This method is called when the thread runs
           public void run()
           {
               log.info("Thread #0 ...", threadNo);
               TestEvent event = new TestEvent();
               event.setDate(Calendar.getInstance().getTime());
               persist(event);
           }
          }
      }
      



      Here is the Entity:


      package com.risk.entity;
      
      import java.io.Serializable;
      import java.util.Date;
      
      import javax.persistence.Entity;
      import javax.persistence.GeneratedValue;
      import javax.persistence.GenerationType;
      import javax.persistence.Id;
      import javax.persistence.SequenceGenerator;
      import javax.persistence.Table;
      
      @Entity
      @Table(name = "tbl_testevent")
      public class TestEvent implements Serializable, Idable
      {
      
          /**
           * 
           */
          private static final long serialVersionUID = -7793151651824653553L;
      
          // seam-gen attributes (you should probably edit these)
          private Long id;
      
          private Date date;
      
       
          @Id
          @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "testEvent_id_sequence")
          // name of the generator
          @SequenceGenerator(name = "testEvent_id_sequence", sequenceName = "TESTEVENT_ID_SEQ")
          public Long getId()
          {
           return id;
          }
      
          public void setId(Long id)
          {
           this.id = id;
          }
      
          /**
           * @return the creationDate
           */
          public Date getDate()
          {
           return date;
          }
      
          /**
           * @param creationDate
           *                the creationDate to set
           */
          public void setDate(Date date)
          {
           this.date = date;
          }
      
      }
      



      And the page:


      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                                   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
           xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:h="http://java.sun.com/jsf/html">
      
      
                <h:form id="progressForm">
                     <div class="actionButtons"><h:commandButton value="Process"
                          action="#{processor.process}" /></div>
                </h:form>
      
      </ui:composition>
      

        • 1. Re: Threadsafe persist in multithread environment?
          hurzeler

          I think one needs to inject the EntityManagerFactory then make a new EntityManager in each thread.


          package com.risk.session;
          
          import java.io.IOException;
          import java.util.Calendar;
          import java.util.concurrent.ExecutorService;
          import java.util.concurrent.Executors;
          
          import javax.persistence.EntityManager;
          import javax.persistence.EntityManagerFactory;
          
          import org.jboss.seam.annotations.In;
          import org.jboss.seam.annotations.Logger;
          import org.jboss.seam.annotations.Name;
          import org.jboss.seam.annotations.Transactional;
          import org.jboss.seam.log.Log;
          
          import com.risk.entity.TestEvent;
          
          @Name("processor")
          public class Processor 
          {
          
              /**
               * 
               */
              private static final long serialVersionUID = -8691085937149866076L;
          
              @Logger
              Log log;
          
              @In
              EntityManagerFactory entityManagerFactory;
              
              @Transactional
              public void process() throws IOException
              {
               log.info("Processing...");
          
               // Spawn threads to optimise the process
               ExecutorService threadExecutor = Executors.newFixedThreadPool(2);
               for (int i = 0; i < 2; i++)
               {
                   threadExecutor.execute(new ProcessThread(i));
               }
              }
          
              class ProcessThread implements Runnable
              {
          
               private int threadNo;
          
               public ProcessThread(int threadNo)
               {
                   this.threadNo = threadNo;
               }
          
               // This method is called when the thread runs
               public void run()
               {
                      log.info("Thread #0 ...", threadNo);
                   EntityManager em = entityManagerFactory.createEntityManager();
                   TestEvent event = new TestEvent();
                   event.setDate(Calendar.getInstance().getTime());
                   em.persist(event);
               }
              }
          }
          



          The only trouble is that the entityManager resolves to null.


          In commponent.xml I have:


          <persistence:entity-manager-factory
                    name="entityManagerFactory" persistence-unit-name="test" />
          <persistence:managed-persistence-context
                    name="entityManager" auto-create="true" entity-manager-factory="#{entityManagerFactory}" />
          



          Just in case you wondering.


          So why does the EntityManagerFactory resolve to null?

          • 2. Re: Threadsafe persist in multithread environment?
            balazska

            hy!


            What was the solution?