5 Replies Latest reply on Jan 11, 2002 10:10 PM by davidjencks

    Exactly 50% of ejbCreate() failing

    cecchet

      I have an application that already works fine with other application servers and I tried to port it to JBoss. I only have a problem with ejbCreate() that first succeeds then fails then succeeds then fails and so on ...
      I use JBoss 2.4.3 and MySQL. For my primary key field, I use the auto_increment facility of MySQL. I have a user table that looks like that :
      CREATE TABLE users (
      id INTEGER UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT,
      firstname VARCHAR(20),
      lastname VARCHAR(20),
      nickname VARCHAR(20) NOT NULL UNIQUE,
      password VARCHAR(20) NOT NULL,
      email VARCHAR(50) NOT NULL,
      rating INTEGER,
      balance FLOAT,
      creation_date DATETIME,
      region INTEGER UNSIGNED NOT NULL,
      PRIMARY KEY(id)
      );

      The ejbCreate() method of the UserBean is as follows :
      public UserPK ejbCreate(String userFirstName, String userLastName, String userNickName, String userEmail,
      String userPassword, Integer userRegionId) throws CreateException, RemoteException, RemoveException
      {
      id = null; // Let the database auto-increment the value
      firstName = userFirstName;
      lastName = userLastName;
      nickName = userNickName;
      password = userPassword;
      email = userEmail;
      regionId = userRegionId;
      creationDate = TimeManagement.currentDateToString();
      return null;
      }

      This way I let the DB assign unique id's. If you implement properly your primary key class to check for null values, it works fine. But with JBoss, on the second call to ejbCreate() (in fact after every call to ejbCreate() that was successfull) I get the following error :
      [User] XAException: tx=XidImpl [FormatId=257, GlobalId=sci20//9944, BranchQual=] errorCode=XA_UNKNOWN(0)
      [User] javax.transaction.xa.XAException: Rollback failed: General error: Warning: Some non-transactional changed tables couldn't be rolled back
      [User] at org.jboss.pool.jdbc.xa.wrapper.XAResourceImpl.rollback(XAResourceImpl.java:219)
      [User] at org.jboss.tm.TxCapsule.rollbackResources(TxCapsule.java:1539)
      [User] at org.jboss.tm.TxCapsule.rollback(TxCapsule.java:394)
      [User] at org.jboss.tm.TransactionImpl.rollback(TransactionImpl.java:88)
      [User] at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:352)
      [User] at org.jboss.ejb.plugins.TxInterceptorCMT.invokeHome(TxInterceptorCMT.java:86)
      [User] at org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:103)
      [User] at org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:106)
      [User] at org.jboss.ejb.EntityContainer.invokeHome(EntityContainer.java:420)
      [User] at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker.invokeHome(JRMPContainerInvoker.java:372)
      [User] at java.lang.reflect.Method.invoke(Native Method)
      [User] at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:241)
      [User] at sun.rmi.transport.Transport$1.run(Transport.java:152)
      [User] at java.security.AccessController.doPrivileged(Native Method)
      [User] at sun.rmi.transport.Transport.serviceCall(Transport.java:148)
      [User] at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:465)
      [User] at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:706)
      [User] at java.lang.Thread.run(Thread.java:484)
      [User] TRANSACTION ROLLBACK EXCEPTION:INSERTING AN ALREADY EXISTING BEAN, ID = edu.rice.rubis.beans.UserPK@0; nested exception is:
      java.lang.IllegalStateException: INSERTING AN ALREADY EXISTING BEAN, ID = edu.rice.rubis.beans.UserPK@0
      [User] java.lang.IllegalStateException: INSERTING AN ALREADY EXISTING BEAN, ID = edu.rice.rubis.beans.UserPK@0
      [User] at org.jboss.ejb.plugins.AbstractInstanceCache.insert(AbstractInstanceCache.java:247)
      [User] at org.jboss.ejb.plugins.EntityInstanceInterceptor.invokeHome(EntityInstanceInterceptor.java:171)
      [User] at org.jboss.ejb.plugins.EntityLockInterceptor.invokeHome(EntityLockInterceptor.java:108)
      [User] at org.jboss.ejb.plugins.TxInterceptorCMT.invokeNext(TxInterceptorCMT.java:135)
      [User] at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:307)
      [User] at org.jboss.ejb.plugins.TxInterceptorCMT.invokeHome(TxInterceptorCMT.java:86)
      [User] at org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:103)
      [User] at org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:106)
      [User] at org.jboss.ejb.EntityContainer.invokeHome(EntityContainer.java:420)
      [User] at org.jboss.ejb.plugins.jrmp.server.JRMPContainerInvoker.invokeHome(JRMPContainerInvoker.java:372)
      [User] at java.lang.reflect.Method.invoke(Native Method)
      [User] at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:241)
      [User] at sun.rmi.transport.Transport$1.run(Transport.java:152)
      [User] at java.security.AccessController.doPrivileged(Native Method)
      [User] at sun.rmi.transport.Transport.serviceCall(Transport.java:148)
      [User] at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:465)
      [User] at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:706)
      [User] at java.lang.Thread.run(Thread.java:484)

      The warning about the rollback failing is ok since I use non-transactional tables in MySQL.
      I don't really understand the problem with the primary keys and especially why it works again just after throwing this exception (something is resetted ?).

      If it can help, here is the UserPK code:
      public class UserPK implements java.io.Serializable {
      public Integer id;

      public UserPK() {}

      public UserPK(Integer uniqueId)
      {
      id = uniqueId;
      }

      public int hashCode()
      {
      if (id == null)
      return 0;
      else
      return id.intValue();
      }

      public boolean equals(Object other)
      {
      boolean isEqual = false;
      if (other instanceof UserPK)
      {
      if (id != null)
      isEqual = (id == ((UserPK)other).id);
      else
      isEqual = (id.intValue() == ((UserPK)other).id.intValue());
      }
      return isEqual;
      }

      }

      Note that in the reference UserPK@0 printed in the stack trace, the 0 corresponds to the hashCode value. If I return a random number instead of 0 if id is null, it still does not work (every 2 calls) but I can see different UserPK@xxx in the stack trace.

      Any help will be greatly appreciated. At least it also works once every 2 calls ! :-)

      Emmanuel

        • 1. Re: Exactly 50% of ejbCreate() failing
          davidjencks

          jboss doesn't support using autoincrement keys/sequences + db triggers or any other way of having your db auto-assign pk values. Unfortunately you have to have the pk value before you try to create the entity bean. There are zillions of posts about different ways to do this.

          I suspect it is a bug that any of your create calls succeed.

          • 2. Re: Exactly 50% of ejbCreate() failing
            cecchet

            > There are zillions of posts about different ways to do this.

            Yes but any other method like using a session bean to manage the pks is way slower !

            > I suspect it is a bug that any of your create calls
            > succeed.

            The strange thing is that each time the INSERT is sent to the database. Only every 2 calls I get an error. As MySQL cannot rollback the data is correctly written to the DB and I can retrieve it later with any finder method.
            I agree with you that any create calls should fail and especially the first one if it is not supported. I think it could work with little effort by looking at what is done when the exception is catched.
            Anyway I don't see why create should fail. If the container didn't reload the info from the DB, I should just have a not updated primary key, that's it. What kind of checking the container tries to do on the pk ?

            Thanks for your help,
            Emmanuel

            • 3. Re: Exactly 50% of ejbCreate() failing
              davidjencks

              > > There are zillions of posts about different ways to
              > do this.
              >
              > Yes but any other method like using a session bean to
              > manage the pks is way slower !

              Even if you use a strategy like fetching an initial value from the db every 1000 requests and handing them out one by one?
              >
              > > I suspect it is a bug that any of your create
              > calls
              > > succeed.
              >
              > The strange thing is that each time the INSERT is
              > sent to the database. Only every 2 calls I get an
              > error. As MySQL cannot rollback the data is correctly
              > written to the DB and I can retrieve it later with
              > any finder method.
              > I agree with you that any create calls should fail
              > and especially the first one if it is not supported.
              > I think it could work with little effort by looking
              > at what is done when the exception is catched.
              > Anyway I don't see why create should fail. If the
              > container didn't reload the info from the DB, I
              > should just have a not updated primary key, that's
              > it. What kind of checking the container tries to do
              > on the pk ?

              Well, it needs all the pks to be different so it can distinguish cached entities from one another. That might run into difficulties if they are all null.
              >
              > Thanks for your help,
              > Emmanuel

              • 4. Re: Exactly 50% of ejbCreate() failing
                cecchet

                > Even if you use a strategy like fetching an initial
                > value from the db every 1000 requests and handing
                > them out one by one?

                You cannot implement a "static" session bean. If I want a session bean to distribute pks to entity beans, every ejbCreate call should contact the same instance of the session bean to get a unique pk. Unfortunately, it is not possible. If I am wrong, I am strongly interested by the solution.
                The code should look like that:
                ejbCreate(...){
                sbHome = ...lookup("PkManagerSBHome");
                // There is no finder for SB so I have to call create!
                sb = sbHome.create(); // I get a new SB each time !
                sb.getNextId(); // should ask the DB and reserve the id
                ...
                }

                This way you always get a new instance of the session bean and it is useless trying to cache anything, isn't it ?
                In fact, it would be easier to directly put the SQL code in ejbCreate() but what's the point of using CMP entity beans if we have to put SQL in them ?

                • 5. Re: Exactly 50% of ejbCreate() failing
                  davidjencks

                  mmm yes, session bean won't work unless you want to break the rules and use a static variable. (This would work even with multiple vms, since each instance of the class would get a different starting value from the db, so no two class instances could ever supply overlapping values). An mbean would work fine, however, and you could access it portably from a session bean.