4 Replies Latest reply on Jan 17, 2007 9:55 AM by Konstantin Ermakov

    Stateful Bean and the database commit

    Konstantin Ermakov Newbie

      Hi!

      I have the following stateful bean:

      
      public class MyStatefulBean implements MyStatefulBeanI, SessionSynchronization {
      
       @PersistenceUnit(unitName = "db")
       public EntityManagerFactory factory;
      
       public MyEntity getObject( int id ) throws Exception {
       EntityManager m = factory.createEntityManager();
       return m.find(MyEntity.class, id);
       }
      
       @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
       public MyEntity update( MyEntity newEntity ) throws Exception {
       EntityManager m = factory.createEntityManager();
       oldEntity e = m.find(MyEntity.class, id);
      
       e.setAttribute1( newEntity.getAttribute1() );
       ...
       ...
       m.flush();
       m.clear();
       m.close();
       }
      
       public void afterCompletion(boolean flag) throws EJBException, RemoteException {
       if( flag == true ) {
       sendJMSMessage()
       }
       }
      
       .....
      
      }
      
      


      As far as I understood, after update() it is not guaranteed, that the data is in database and commit event already happened. So, as I have the fat clients it can be the case, that after update() was called, getObject() still returns the old entity.

      My question is - is it guarateed, that after the afterCompletion() method the database commit already happened, and it is safe to call getObject() to receive the new data?

      Thank you,
      Konstantin

        • 1. Re: Stateful Bean and the database commit
          Alex Grönholm Novice

          To my knowledge, m.flush() guarantees that the database commit happens before the method returns (or else it will throw an exception).
          I can't understand why you inject an EntityManagerFactory instead of an EntityManager.
          What is the purpose of afterCompletion()? And why is the bean stateful and not stateless? I don't see any state variables that would warrant a stateful bean.

          • 2. Re: Stateful Bean and the database commit
            Konstantin Ermakov Newbie

            Hi!

            No, flush() does not mean commit. There was already a topic and we have a lot of problems with this issue. See the following topic:
            http://www.jboss.com/index.html?module=bb&op=viewtopic&t=96458&postdays=0&postorder=asc&start=10

            I have stateful bean, because SessionSynchronization is not appliable for Stateless Bean. And actually, this is the point - if afterCompletion guaratees us, that the transaction was commited.

            • 3. Re: Stateful Bean and the database commit
              Jens Elkner Apprentice

               

              "konstantin.ermakov" wrote:
              Hi!

              As far as I understood, after update() it is not guaranteed, that the data is in database and commit event already happened. So, as I have the fat clients it can be the case, that after update() was called, getObject() still returns the old entity.

              My question is - is it guarateed, that after the afterCompletion() method the database commit already happened, and it is safe to call getObject() to receive the new data?


              I think yes, when you use TXed messages to notify your clients. E.g. here the meat (aka main things) how I use it:
              public class MsgProducer {
               private static HashMap<String, AtomicLong> seq =
               new HashMap<String, AtomicLong>();
              ...
               /**
               * Simple container for recording queue parameters.
               *
               * @author Jens Elkner
               * @version $Revision$
               */
               public static class QueueParams {
               Connection con;
               Session session;
               MessageProducer producer;
              
               QueueParams(Connection con, Session session, MessageProducer producer) { this.con = con;
               this.session = session;
               this.producer = producer;
               }
              
               void destroy() {
               if (con != null) {
               try {
               con.stop();
               } catch (JMSException e) {
               // ignore
               }
               }
               if (session != null) {
               try {
               session.close();
               } catch (JMSException e) {
               // ignore
               }
               }
               if (con != null) {
               try {
               con.close();
               } catch (JMSException e) {
               // ignore
               }
               }
               }
               }
              
               /**
               * Create a message producer for the given destination.
               *
               * @param destination topic to use
               * @param tx whether to use a transaction scoped session
               * @return <code>null</code> on error, the message producer otherwise.
               */
               private static QueueParams createNewSession(String destination, boolean tx)
              {
               try {
               ctx = new InitialContext();
               // we let the destination decide, whether it is a
               // Queue- or TopicConnectionFactory
               ConnectionFactory cf = (ConnectionFactory)
               ctx.lookup("java:/JmsXA");
               // create a Connection using the ConnectionFactory (it is not
               // necessary to start the Connection if messages are only sent)
               Connection tc = cf.createConnection();
               // create a Session using the Connection: Transaction +
               // Auto Acknowledge of the Messages
               Session s = tc.createSession(tx,
               tx ? Session.SESSION_TRANSACTED : Session.AUTO_ACKNOWLEDGE);
               // look up the Destination (either Queue or Topic) to which we want
               // to connect in JNDI
               Destination dst = (Destination) ctx.lookup(destination);
               // create a message producer by the session and destination acquired
               // above
               MessageProducer producer = s.createProducer(dst);
               if (producer != null) {
               params = new QueueParams(tc, s, producer);
               }
               } catch (Exception e) {
               log.error(e.getLocalizedMessage());
               failed = true;
               }
               if (failed && params != null) {
               params.destroy();
               params = null;
               }
               return params;
               }
              
               /**
               * Send a message to the given destination.
               *
               * @param destination name of the queue/topic - e.g. "topic/foo" or
               * "queue/bar".
               * @param list list of CRUD events
               */
               public static void send(String destination, CrudEventList list) {
               if (destination == null || list == null || list.getSize() == 0) {
               return;
               }
               QueueParams q = createNewSession(destination, true);
               if (q == null) {
               log.error("Unable to deliver message to '" + destination + "'");
               return;
               }
               try {
               ObjectMessage m = q.session.createObjectMessage(list);
               long msgId = getNextId(destination);
               m.setLongProperty("seq", msgId);
               if (log.isDebugEnabled()) {
               log.debug("sending CRUD message " + msgId + " to " + destination);
               }
               q.producer.send(m);
               } catch (JMSException e) {
               log.error(e.getLocalizedMessage());
               }
               q.destroy();
               }
              ...
              }
              

              So if you append something like MsgProducer.send(bla, fahsel) at the end of your SLSB, the message would be sent only, if the TX succeeds, i.e. commit succeeded.
              Never had any out of sync problems, after changed to this pattern, so it might be useful for you as well... ;-)

              • 4. Re: Stateful Bean and the database commit
                Konstantin Ermakov Newbie

                Hi!

                My Idea is to send or not to send a message according to the boolean parameter in the afterCompletion method. But thank you for the code. So far I have not seen any problems with afterCompletion event as well, but I want just to be sure, if it is fine, there is nothing specific about it.