Discussions

J2EE patterns: Entity Bean Primary Key Generator

  1. Entity Bean Primary Key Generator (65 messages)

    A simple solution to the primary key generation problem is to use an entity bean that increments an internal counter. Since this primary key generator is an entity bean, it is distributed, transactional, and persistent. Simple!

    The entity bean maintains a counter starting from 1, and has a string primary key. Clients can thus create primary key creators using any string and find them again using that same string. The UniqueIDGeneratorBean returns integers, incrementing them by one each time getNextID() is called.

    Complete Code for the Primary Key Generator Bean (using EJB 1.0):
     
    /**
    * This Entity Bean generates unique id's for any client that needs one.
    *
    * @author Floyd Marinescu
    */
    public class UniqueIDGeneratorBean implements EntityBean {


    protected EntityContext ctx;

    // Environment properties the bean was deployed with
    public Properties env;

    private String idName;
    private long nextID;


    /**
    * Create a PK generator.
    *
    * @param idName the name for this unique id tracker
    * @return The primary key (just a string) for this index
    */
    public UniqueIDGeneratorPK ejbCreate( UniqueIDGeneratorPK key ) throws CreateException, RemoteException
    {
          this.idName = key.idName;
          this.nextID = 0;

          ...

          //Build SQL query
          pstmt = conn.prepareStatement("insert into uniqueIDs (idName, nextID) values (?, ?)");
          pstmt.setString( 1, this.idName);
          pstmt.setLong( 2, this.nextID);
               
          //insert row in databse
          pstmt.executeUpdate();

          return key;

          ...
    }

    public UniqueIDGeneratorPK ejbFindByPrimaryKey(UniqueIDGeneratorPK key) throws FinderException, ObjectNotFoundException, RemoteException
    {
          ...

          // Find the Entity in the DB
          pstmt = conn.prepareStatement("select idName from uniqueIDs where idName = ?");
          pstmt.setString( 1, key.idName );
          rs = pstmt.executeQuery();

          // iterate to the first row in the resultset
          if( ! rs.next() ) //if generator does not exist in database
          {
               throw new ObjectNotFoundException("ID Generator " + key + " does not exist in databse" );
          }

          // No errors occurred, so return the Primary Key
          return key;
          ...
    }

    public void ejbLoad() throws RemoteException
    {
          // Query the Entity Context to get the current
          // Primary Key, so we know which instance to load.
          this.idName = ((UniqueIDGeneratorPK) ctx.getPrimaryKey()).idName;

          ...

          pstmt = conn.prepareStatement("select nextID from uniqueIDs where idName = ?");
          pstmt.setString(1, this.idName);

          rs = pstmt.executeQuery();

          // iterate to the first row in the results
          rs.next();

          // populate with data from database
          this.nextID = rs.getLong("nextID");

          ...
    }

    public void ejbRemove() throws RemoteException
    {
         // Query the Entity Context to get the current
         // Primary Key, so we know which instance to load.
         String pk = ((UniqueIDGeneratorPK)ctx.getPrimaryKey()).idName;
     
         ...

         pstmt = conn.prepareStatement("delete from uniqueIDs where idName = ?");
         pstmt.setString(1, pk);

         //Throw a system-level exception if something bad happened.
         if (pstmt.executeUpdate() == 0)
         {
              //log errors here
              throw new RemoteException("UniqueIdGenerator " + pk + " failed to be removed from the database");
         }

         ...
    }


    public void ejbStore() throws RemoteException
    {
         ...
         
         // Store account in DB
         pstmt = conn.prepareStatement("update uniqueIDs set nextID = ? where idName = ?");

         pstmt.setLong( 1, this.nextID);
         pstmt.setString(2, this.idName);
         pstmt.executeUpdate();

         ...
    }


    public long generateUniqueId() throws EJBException
    {

          this.nextID++;
          return this.nextID;

    }

    The bean is very versatile, and can be used in many different ways. The way I use it is that each entity bean "class" in my application uses a different instance of the UniqueIDGeneratorBean. In the create method of an entity bean class (for example, MessageBean), I would execute:

    UniqueIDGeneratorHome.findByPrimaryKey("MessageBean");

    Which will return an instance of the UniqueIDGeneratorBean that is maintaing count just for my MessageBean class.

    If you want primary keys to be unique across all beans in your application, then simply use one instance of your UniqueIDGenerator across your entire application.

    *******************************************
    Editor's note (October 5, 2000).

    Hi everyone. It has been pointed out that this pattern is infact not portable across application servers, as it assumes Pessimistic Concurrency at the app.server/DB level. See TheServerSide.com newsletter #2 for more information about Optimistic Vs. Pessimistic Concurrency.

    Since posting this pattern, this thread has grown to include many other good idea's about primary key generation strategies, so please read on.

    *******************************************

    Threaded Messages (65)

  2. The transaction isolation level for getNextID() depends on your application server. Most app. Servers will have one instance of an entity bean, for each row in the database. That is, there won't be two separate instances of an entity bean in memory, with the same primary key. For these types of app. Servers, I believe that a transaction setting of READ_COMMITTED should suffice. That's because if client A is currently executing getNextID(), and client B does an ejbFind() on the UniqueIDGeneratorBean with the same primary key, then the application server will wait until client A is done, and then pass that same instance of UniqueIDGeneratorBean to client B. This is how entity bean cache's work. In this scenario, each UniqueIDGeneratorBean INSTANCE is effectively a Singleton.

    Now for crazy advanced application servers that allow multiple instances of an entity bean that contain the same primary key (multiple EJB's mapping to the same ROW in the database), then I think that a transaction setting of SERIALIZEABLE is required, since it is not guaranteed that all clients of a particular "instance" of an entity bean, will use the same entity bean (no Singleton guarantee).

    For higher performance, make each entity bean in your application use its own instance of the UniqueIDGeneratorBean, then you can distribute generation of primary keys across multiple instances of the UniqueIDGeneratorBean.
  3. Floyd,

    is this method going to work when you have multiple app server instances going for load balancing ? In this case, unless there is shared object cache (which I don't think is the case for WebLogic and Websphere for example), I think each app instance will instantiate it's own copy of the original unique entity bean and go their own way from there.This could mess things up bigtime

    Please correct me if I'm wrong,

    cheers,

    Eddie Fung
  4. I think the method should work fine, because each app. server's still rely on one database. Thus setting a the transaction isolation to SERIALIZEABLE will cause locking at the database level, which will propagate to the app. servers to ensure that there won't be any concurrent invocations of this bean.

       I think any server that is capable of load balancing should have some sort of distributed transaction manager that would take care of this.

      in either case, TX_SERIALIZEABLE will guarantee consistency.

    Floyd

  5. Floyd,

    whilst this is true, when the app servers start up and do their initial load of the entity bean, all app server instances will get the same bean (or a version that was true at start up) from the database. Now my understanding was that the entity beans in each instance will be different ie. they are separate instances of an entity bean and not a single, shared instance. When the session bean is run, it could run on any app server instance eg.

    App Server Instance 1 : Starts at 0900 hours, reads entity bean with value 17. At 0905, the bean is used several times and eventually is ejbStored with value 23.

    App Server Instance 2: Starts at 0906 , creates entity bean by reading database and ejbCreates entity bean with value 23.

    App Server Instances 1 and 2 now have 2 versions of the entity bean. At 0906 each of them has the value of 23 in memory. Each app server instance will then start incrementing from the same number so key value 24 could be used and applied by both app server instances as the entity bean is not cached across the app server instances.

    Please correct me if I am wrong but this is the way that I thought that both WebLogic and Websphere would work ..

    cheers,

    Eddie Fung
  6. Entity Bean Primary Key Generator[ Go to top ]

    I think this is right.Floyd,your solution will probably work when a single server instance is running.However,if multiple server instances were up,then there is a strong danger of two primary keys being generated the same as different server instances running at the same time.Two instances of UniqueIDGeneratorBean on two different servers could generate the same primary key.
  7. Entity Bean Primary Key Generator[ Go to top ]

    Moreover,TRANSACTION_SERIALIZABLE isolation level could make performance level to drop appreciably.
  8. Eddie and Anuj,

      If your transaction isolation level is set to tx_serializeable, I still don't see how this could cause problems in a clustered environment, because:

    1) At the start of a transaction, the entity bean will ejbLoad itself. At this point, if ou are using, SERIALIZEABLE, there should be a read lock in the database, or the app. server, or both. So another instance of the PK Generator(on some other machine) will have to wait until this instance is finished.

       Thus app. server 1 and 2 will execute one at a time,and will always have the most up-to-date data in memory, because they will call ejbLoad before every invocation of getNextID().

       Also, it is true that serilizeable will cause performance issues, thats why I recommend having each entity bean "class" use its own instance of the PKGenerator, this should help a lot (rather than the whole app. share a single instance, a single counter for primary keys). For applications that create thousands of entity beans a second, this may still be an issue, but I think 70% of apps. will find this pattern to be more than useful, considering its ease of implementation and portability.

    thanks for your input guys.

    Floyd
  9. Floyd,

    I see what you are saying re: the ejbLoad refresh. However is it in the EJB spec that entity bean serialisation across app server instances is mandatory ? If it isn't and the database table gets a read lock , then the scenario that we have been discussing is still possible since database read locks would permit multiple reads. Granted that the update from the first instance has to finish its work before the second one can, but this would cause problems. Actually could this cause a problem depending on how the database software acquires, escalates and releases locks ?? I think that the two reads would occur, the first one would then escalate the lock into an update lock and perform the update and the second one could not perform the update until the first one has committed. Anyone out there know ??

    I am not saying that you are wrong but merely that I don't know whether the spec mandates that what you are saying must happen.

    cheers,

    eddie
  10. Gents,

    Q:'However is it in the EJB spec that entity bean serialisation across app server instances is mandatory ?'

    A: It depends on the vendor implementation. If app server vendor implements Commit Option C, the question looses validity as the container does not cache objects between transactions. Whereas if the vendor implements Commit Option A and caches objects between transactions, cache synchronization is vendor dependent.

    Also the EJB spec doesn't mandate how databases implement locking.
    As Floyd pointed out, the first instance performs the update, the second one synchronizes itself with the db contents, if isolation level is set to 'Serializable.'
  11. This is INCORRECT.

    Read Write entity beans are can NOT reside on two different clustered servers at once. They are "PINNED" to a server.
  12. I'm sorry to disappoint you, but this in not the case, it depends.
    e.g. BEA WebLogic uses pessimistic transactions by default (I think you cannot alter this), but Borland AppServer uses optimistic ones (which is normally a _lot_ faster).
    In general we could say that the EB should _seem_ to be pinned to one server, but of course it will be instantiated on more than one server, it is duplicated/cached for performance reasons. As long as the EB is not modified this is no problem, but if it gets modified the other instance needs to be altered.
    With pesssimistic transactions this is no problem as you can easily see, but with optimistic ones it can get apparent if two clients modify the EB (no matter if on one or two servers).
    Bottom line: The application server is free to instantiate the entity bean on as many servers as many times it likes, Borland AppServer does this (you can see it in the graph), and it really performs well with this strategy.

    Messi
  13. This is incorrect.

    Entity Beans are "PINNED" to a server.

    The JNDI lookup returns the same exact instance of a read write bean everytime. Even in a clustered environment. None of the vendors support replication of read/write entity beans yet.
  14. Entity Bean Primary Key Generator[ Go to top ]

    Here is the scenario: I have a legacy as well as java based application accessing the informix database.

    Primary key field is defined as Serial type to ensure uniqueness of the values.

    Problem is how do i handle such situation as serial data type is not supported by EJB.
  15. Entity Bean Primary Key Generator[ Go to top ]

    This means that the method that Floyd has proposed is non EJB server independent which is a problem.

    Having the bean update the database after each key is used is going to be a big overhead...

    There is an article (pointed out by James Cook) which does partly address this problem of frequent db updates in a Surrogate key generator (see http://www.sdmagazine.com/uml/thinking/s9912to.shtml) but there are some outstanding questions regarding it..
  16. Eddie, thanks for linking to Scott Ambler's article, it gave me a great idea for changing this pattern to make it wonderfully scalable and non-vendor dependant(for commit option c).

    How about the following change to the entity bean:
    1) On ejbLoad, get the next HIGH value from the database.
    2) On every call to getNextID(), just increment the internal variable (but don't store anything in the database).
    3) Set a high timeout for this bean, maybe 20 minutes or something.

       This modification will solve all the problems discussed in this thread and make the pattern super fast:

    Problem One:
       Serialization across app. servers
    Solution:
       Now every app. server can have its own instances of the entity bean, which will run independantly, since in each ejbLoad, each bean will have a different HIGH loaded into it.

    Problem Two:
       Performance implications of updating DB after each pk is generated.
    Solution:
        Generating keys no longer requires updating the database. No two beans in the same app. server or other app. servers will use the same HIGH value, so there is no chance of duplicate PK's.

    Floyd
  17. Floyd,

    now you're talking ! I think it does get around the problems that we have discussed previously.

    I am actually thinking of doing a variation on the HIGH/LOW approach so that I can create surrogate keys with a more even distribution and hence reduce the chance of getting 'hotspots' on index page updates which is the problem with just getting a 'next available' sequence number approach..

    cheers,

    eddie
  18. Floyd
    Excuse the Ignorance but what's a HIGH variable and how do you obtain it? Could you give an example in code of what your new pattern would look like I'd greatly apprec.
    thanks.
    Jeff
  19. Jeff,

       The HIGH/LOW approach to generating primary keys was explained in Scott Amblers article, which I linked to in my last post, before talking about HIGH/LOW. Rather than re-explain the concept, I hoped people would follow the link and read Scott's own explanation.

       As for some source code, well I havn't actually used this new method yet so I have none. :)

      You can read Scott Ambler's article here.

    Floyd
  20. I did implement the High/Low Primary Key generator. It worked great. It used the database to store the HIGH key and it load it for each instance of the EJB.

    This works great for Clustered environment since the HIGH/LOW factory is Singleton. The bean is just a conduit for that factory.

    cheers
    Dan D
  21. Floyd,
      Please could You please correct Scott Ambler's article location.
    It is not exists on ted location

    Thanks
  22. Boris,

    Scott Ambler article location is http://www.sdmagazine.com/articles/1999/0012/0012p/0012p.htm?topic=uml

    Emmanuel
  23. It's moved - sdmagazine have renamed things. You should now look here:

    http://www.sdmagazine.com/articles/1999/9912/9912p/9912p.htm

    c
  24. Note : u can find the Scott Ambler's article in this new url
  25. Problem in Weblogic (and probably in other servers as well) the ejbLoad() will be called prior to every call to nextID() unless the object is read only then it will only be called at load time, but with this design it cannot be a read only object.
     

  26. Correction Weblogic would call ejbLoad() at the begining of each Transaction and ejbStore() on every commit

    Dan D
  27. but with this design it cannot be a read only object.


      Daniel, why can't the object be readonly? Once the bean is ejbLoaded() it has a new high value, and all subsequent calls to getNextID() will no longer need to persist the current ID to the database. The EJB will just pass out the next available ID in its internal instance. There is no reason why this variable needs to be persisted. Infact, there is nothing to persist. The DB Table won't contain a "nextID" column, just a next "HIGH" column, which is only to be read at ejbLoad time anyway. So the bean is "physically" read only, even though it is incrementing its internal counter all the time.

      So why do you say that the bean can't be read only?

    Floyd
  28. Floyd for the most part I was reffering to the example code above not being able to be read only.

    But even in what you are saying at some point the High key will have to be bumped .. ie after the first call to ejbLoad() or when you run out of LOW values (it could happen:) ).
    This means the HIGH data is written to the data base at some point therefore it cannot be truly read only.
    I used a Oracle Sequence to do my implementation of the HIGH query so it simulates a Read only attribute. I am not sure if it truly is but it works like one. So I can get away with ejbLoad being called once. and using a query to bump the value, but that is not portable as the example is.

    I also use the NOTSUPPORTED transactional definition for my Primary Key generator EJB. So the Oracle sequence read is done outside of any transaction. So If you bump a High key it will stay bumped and never be rolled back even if the Transaction fails. But with (2 billion integers HIGH * 2 billion LOW) it is a good bet that you will not run out of them anytime soon.

    A portable solution would have to have its own transaction level. Since there is not a standard sequence or autoicrement in SQL. There would have to be a read of the HIGH value increment it by one then a write.

    I also used a 32 bit Integer HIGH and LOW. The key is then able to be represented in lots of ways i.e. a 20 char String, a long, or a complex struct with 2 32 bit integer members. So whatever your database is most efficient at storing and sorting can be used for primary keys. I chose to store strings because of the human readability factor.

    Cheers
    Dan D

  29. To me the primary problem with the HIGH/LOW scheme is that the object which dispenses the actual UID has to be "alive" for long enough to use quite a few LOW values, otherwise the approach reverts back to the brute-force "hit the database for an id everytime" algorithm.

    It seems to me that the lifetime of any type of EJB as well as the number of EJB instances varies from server implementation to implementation, so that even if a pure
    EJB implementation is portable, it is not guaranteed to be
    at all efficient.

    Here's my idea (comments would be highly appreciated
    since this is what I am coding up right now)

    Put the SQL to get and update the HIGH value into a stateless session bean (not sure what transaction isolation level to use, but SERIALIZABLE is probably ok since this call will not be made often).

    Next use a standard Java singleton class to handle dispensing the LOW values. E.g.
    ----------------
    public class UIDDispenser
    {
        private static UIDDispenser dispenser;

        private int high;
        private int low;

        private UIDDispenser()
        {
            low = 0;
            // get high from the stateless session bean
        }

        public static synchronized UIDDispenser getDispenser()
        {
            if(dispenser == null)
                dispenser = new UIDDispenser();
            return dispenser;
        }

        public synchronized long getNextId()
        {
            if(low == Integer.MAX_VALUE)
            {
                low = 0;
                // get high from session bean
            }
            long id = (long)high;
            id *= 0x100000000l;
            id += low++;
            return id;
        }
    }

    //To get the id :
    //UIDDispenser.getDispenser().getNextId();

    -----------------

    This is portable, and on top of that you are guaranteed to have 1 UID dispenser object for the lifetime of the server, so you get very good utilization of LOW values.

    The only overhead involved comes from the synchronization of the getNextId() method, and I am hoping this is insignificant compared to the overhead of going through the EJB container (and possibly an ejbLoad()) every time you want an id.

    Any input much appreciated,
    Serge Monkewitz




  30. Hi Serge,

    >It seems to me that the lifetime of any type of EJB as well
    >as the number of EJB instances varies from server
    >implementation to implementation, so that even if a pure

        The setting of an entity bean time out is a super basic feature, which (though I don't know for sure), I am confident pretty much every app. server would carry. To not have a timeout would mean that you could not support entity bean caching, which is the primary value add that an entity bean container can provide.

         Thus I think we can safely say that the entity bean HIGH/LOW implementation is portable and efficient.
       
         Your design however, is pretty cool. Your method is very efficient since you can now talk to a local object via statics rather than to an EJB all the time. I would think that synchronized method would still be faster than going through the container, however I am unsure as to how portable that is. Some servers (I heard SilverStream does this) creates and destroys new VMs dynamically. If that is so, then how that affect your pattern?

    Floyd
  31. Dan,

      You mentioned that the entity bean could not be truely read only because at some point, its internal LOW could max out, requiring reading a new HIGH from the database.

      My simple solution is to have a method on the bean called "getHigh()" (no pun intended) that would get the next high and increment HIGH counter in the database.

      If you marked getHigh() to be TX_SERIALIZEABLE and TX_REQUIRES_NEW then consistency will be maintained. This is not a major performance problem, since this method will be rarely called.

      Would this work? I wonder could a method with these transaction settings be called from ejbLoad (so as not to duplicate code), also, does anyone know what the transaction settings are for the ejb api methods (load, store, etc)??

    Floyd
  32. It seems to me that you don't need to worry about
    calls to ejbLoad, etc., if you use a stateless session bean.

    The SSB would have 2 local static variables, Low and High,
    both initialized to -1. It would have a method, getUnique.

    On the first call to getUnique, initialize High by a call
    to an entity bean method getHigh, which is declard to
    use SERIALIZABLE and RequiresNew, and set Low to 0.

    Each subsequent call will increment Low. If the value of
    Low gets incremented past its allowed range, then reset
    it to zero, and reset High with a new call to getHigh.

    Is it a correct assumption that local static variables
    in a stateless session bean persist for the duration of
    the bean's life-cycle in the active pool?

    Jason
  33. Jason, if you are going to go the purely stateless session bean approach, then you wouldn't need an entity bean for getHigh(). This method would be on the SSB, and would contain the necessary JDBC to get and increment HIGH in the database. The transaction and isolation levels would ensure consistency.

    As for your approach, it won't work because static member variables are not allowed by the EJB SPEC. You would have to have multiple session beans each with their own unique HIGH and LOW settings.

       There seem to be 3 methods for doing PK generation with HIGH/LOW in discussion here. I am not sure as to which is the best:

    1) HIGH/LOW from an entity bean
    advantages
     - different applications can "find" different PK generators
     - its EJB centric (no statics, simple and portable)
     - the entity bean can become a singleton for an entire cluster, or per server
    disadvantages

    2) HIGH LOW from a stateless session bean
    3) HIGH/LOW from a local static singleton which talks to a SB for new HIGHs.

      I am unsure as to which is really better. Thoughts?

    Floyd
  34. Floyd,

       Wow that's pretty strange (do you know why it does this?) Anyhow, assuming VMs are popping up all over the place, each VM will get one singleton. This doesn't matter since the database still guarantees unique HIGH values across VMs/machines etc...

    Cheers,
    Serge



  35. Actually,

      Gemstone also pops VM's in and out, and the reason is so that you can better utilize your hardwares capability.

      For example, imagine you have a quad processor Sun monster with 4 gigs of RAM. An app. server running in one VM simply won't be able to utilize all of this. There are two ways you can utilize more of your hardware's potential:

    1) manually startup multiple instances of the server in a cluster (all on the same box).
    2) Use an app.server that can dynamically startup and destroy more VM's depending on the load.

       Solution #2 is a million times better than solution one.

    Floyd
  36. Option 3) could be upgraded
    to support some of the nice EB features as follows :

    ----
    public class UIDDispenser
    {
        private static HashMap generators = new HashMap;
    ...
        private UIDDispenser(String gen) throws UIDException {
            /*use EB or SB to retrieve high value for the
            generator with name "gen". Throw an exception
            if this generator name does not exist in the
            database.*/
        }

        public static synchronized UIDDispenser getDispenser(String gen) throws UIDException {
            if(generators.containsKey(name))
                return generators.get(name);
            else {
                UIDDispenser obj = new UIDDispenser(gen);
                generators.put(name, obj);
                return obj;
            }
        } ...
    }
    -----------
        
  37. daniel,

       is it possible for you to share your code with me. my email-id is srinipyaol at yahoo dot com

    thanks
    srini
  38. At least for my application, I'm pretty sure this solution will work just fine (see code example below).

    This strategy puts some burden on the code requesting the ID to handle and retry duplicate keys, but that seems a little trade-off for the simplicity. This code is in my SessionBean facade, which is responsible for generating the ID's for all the entity beans it "manages".

    Does anyone see any serious problems with this?

    private Long getNewPrimaryKey()
    {
        return new Long(System.currentTimeMillis());
    }

    public Long addOrganization(OrganizationModel model)
        throws javax.ejb.CreateException
    {
        int retries = 0;
        for (;;)
        {
            model.setOrganizationId(getNewPrimaryKey());

            try
            {
                return orgLocalHome.create(model).getOrganizationId();
            }
            catch (DuplicateKeyException e)
            {
                if (retries++ >= DUPKEY_RETRIES_MAX)
                    throw new CreateException("Still getting duplicate key after max retries");
            }
        }
    }
  39. How about your link? Does it work?
  40. Scott Ambler article[ Go to top ]

    You can find the Scott Ambler article here http://www.ddj.com/184415770. At right now it’s working but if not found in future than you can get help from GOOGLE genie.
  41. Floyd,

    Although this is a nice pattern, I generaly prefer to generate keys without having to deal with defining services and transactions. Thus, I have devised the following class to use as an OID, or primary key.

    package com.gemstone.gps.services.oid;

    import java.math.BigInteger;
    import java.net.InetAddress;
    import java.io.DataInput;
    import java.io.DataOutput;
    import java.io.IOException;
    import java.util.StringTokenizer;
    import java.util.Random;


     
     public class OIDImpl { //implements OID {

        // when was the last time we created a OID.
        protected static long lastCreateTime;
        // need to synchronize on the creation of OIDs
        protected static Object mutex;
        // high order bits of the OID
        protected static BigInteger HighOrderBits;
       
        // we need a java.lang.Number implementation for Oracle.
        protected BigInteger oid;

        // complicated initialization of the high order bits.
        static {
            try {
                BigInteger eight = new BigInteger(
                              new Integer(8).toString());
                BigInteger two = new BigInteger(
                              new Integer(2).toString());
                mutex = new Object();
                Long random = new Long(
                      new Random(
                      System.currentTimeMillis()).nextLong());
                HighOrderBits = new BigInteger(
                      random.toString());
                StringTokenizer t = new StringTokenizer(
                      InetAddress.getLocalHost().
                                  getHostAddress(),".");
                while ( t.hasMoreTokens())
                    HighOrderBits = HighOrderBits.
                           multiply(eight).
                           add(new BigInteger(t.nextToken()));
                HighOrderBits = HighOrderBits.multiply(
                           new BigInteger(
                              new Long(Long.MAX_VALUE).
                              toString())).multiply(two);
               lastCreateTime = System.currentTimeMillis();
            } catch (Exception e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
        
        public OIDImpl() {
            long now;
            synchronized(mutex) {
                while ((now = System.currentTimeMillis()) ==
                              lastCreateTime);
                lastCreateTime = now;
            }
            oid = HighOrderBits.add(
                new BigInteger(new Long(now).toString()));
        }
        
        /**
         * OIDImpl(foreign)
         *
         * A helper constructor used by read to create a new
         * version of an existing OID object.
         *
         * @param foreign - byte array representing the OID
         *
         */
        
        public OIDImpl(BigInteger foreign) {
            oid = foreign;
        }
        
        /**
         * next()
         *
         * Convenience method to create OIDs.
         *
         */
        
        public static OIDImpl next() {
            return new OIDImpl();
        }
        
        public boolean equals(Object target) {
            return oid.equals(target.toString());
        }
    }

    I sniped out a bunch of stuff so the class might fit this
    format. So, why can a get an unique number from this class? The answer is that I use spacial information to allow me to represent physical and time distance. The measure of physical seperation is IP address. But, this in not granualr enough and must be agumented with a process id. Time seperation is of course, measured in milliseconds. A one-up counter is used to augment this measurement. Unique IDs are constructed from a composite of all four measurements.

    Using this method allows me to forget about all other concerns.

    Kirk Pepperdine
    kcpeppe at sprintmail dot com
        
  42. Kirk,

    First of all you mention creating the OID based on four measurements. I see only 2 in your code, IP address and timestamp... Am I correct in assuming you left the others (you mention PID) out to make the code fit the message board? If you are using a PID, how do you get it from Java in a platform independent way?

    Anyway I have one concern with your approach, which comes
    from the time-based nature of your keys. It seems to me that your method depends on the system clock never being set backwards. Your code reminds me of the java.rmi.server.UID approach to generating unique keys, and they do state the above as a validity condition for the generated keys.

    Am I being paranoid about that issue? I realize that
    the database is vulnerable to "accidental" modification as well, but using a database seems to offer more control than
    having to trust a system clock.

    Regards,
    Serge



  43. Yes, turning back the clock is a concern but, on nearly all of the systems I delt with, the probability of this happening is although (cringe)non-zero, is not high. But having the clock turned back would cause problems inserting data into tables. The other elements (except host independent getPID()) are in the code.
  44. Hi i have faced similar problems before ... correct me if i am off context but i use a Database-Centric method of solving the problem. I have posted my solution in thread 572: Creating DB entries using weblogic under the discussions section of this site.

    I appologise for the typos and would be thank ful if someone put it in a pattern for the readres.

    Happy programming!

    Adi
  45. I created one of these for my crop of EJBs. I designed all of my EJBs to use a java.lang.Integer as the primary key.

    I'm currently just demoing, so I'm using Cloudscape as my database. I use a particular row of a table to store my next allocated key. Under Oracle, I'd build something similar around a sequence (and perhaps use a stored procedure).

    As implemented, I have a PROP table with columns NAME and VALUE. To allocate a key, I get the current VALUE for a specific name ("next-key") and increment it and write it back to the table.

    My key allocator is a stateless session bean. However, it maintains some operational (not conversational state). Translation: when it goes to the database to allocate a key, it allocates a block of keys, which is will then dispense without going to the database. So, instead of incrementing next-key by one, I increment it by some block size (this is all for efficiency ... reducing database access and contention).

    I only care that my keys are unique, I don't care if they are ascending or if there are gaps.

    If I allocate two keys, they may go to different bean instances, and I'll get two very different keys. So be it.

    If my app server crashes, or it destroys some previously active key allocator beans, then the keys pre-allocated by the beans are lost. So be it.

    Alas, the code for this makes the message too long. I think you get the idea, and I'll be releasing the code as one of my examples for Tapestry shortly (in a few weeks).
  46. One pattern I have seen for this is the creating of Primary key pools. Essentially a table which contains possible next primary keys. When you need a key, it is deleted from the table, which causes a lock and assures the key isnt read by the next person. I think I can find a whitepaper on this. You then create an entity bean with a finder that returns the next key. In hte finder method you do the work to delete the row read it etc. Then just dont implement load or store. Maybe thats too simplisitc?

    In EAS since we support alot more then EJB we just create our own GID generator. Not portable of course.

    Dave Wolf
    Internet Applications Division
    Sybase
  47. About that code, it part of the examples for Tapestry.

    You can view the source code online using CVS.
  48. turning back the clock is a concern but,

    >>on nearly all of the systems I delt with,
    >>the probability of this happening is although
    >>(cringe)non-zero, is not high.

    What about Daylight's Savings Time? ;)
  49. What about Daylight Savings Time?


    I thought that DST merely changed the string representation of the time, not its long representation? i.e. "1:59:59" from 100000000, "1:00:00" from 100000001 (note: the numbers chosen are completely unreliable--i'm not going to figure out the proper milli count :).
  50. Is there any problem in using the java.rmi.server.UID.toString() method to generate unique ID's? (be sure to use 'new' each time, or you'll get the same id everytime) According to the API docs, it is supposed to create a unique id with regards to the server. Concatenate that with an ip, or some server identifier, and you should have a pretty good identifier. I'm about to implement this into a project -- does anybody see a problem with this?

    I have created a utility class with a static method that will generate UID's like this:

    public static String getUniqueId() {
    //The RMI Server UID class generates Unique ID's
    UID uid = new UID();

    return uid.toString();
    }
    public static String getUniqueId(String prefix) {
    //Call the no-param method, and prepend it with the prefix and a ":"
    //Using a StringBuffer for performance boost in String contatenation.
    StringBuffer sb = new StringBuffer(prefix).append(":").append(getUniqueId());
    return sb.toString();
    }

    This seems to work ok for me....it just generates a String uniqueId.
  51. Hi Jason,

    here's a variant of your utility class that uses an InetAddress object to set up the prefix:

    import java.rmi.server.*;
    import java.net.*;

    /**
     * Generates a global unique ID from a java.rmi.server.UID
     * object. Intended to be used in an EJB environment, most
     * probably from a Stateless Session Bean.
     *
     * @author Cristina Belderrain
     */
    public class UIDGenerator {

      /**
       * Generates an UID object and returns it as a String.
       * Such an identifier is unique with respect to the
       * host on which it is generated.
       */
      private static String getLocalUniqueId() {
          UID localUID = new UID();
          return localUID.toString();
      }

      /**
       * Generates a global unique ID by concatenating the
       * local unique ID generated by getLocalUniqueId()
       * with the local host address.
       */
      public static String getGlobalUniqueId() {
          InetAddress lHostAddress = null;
          try {
              lHostAddress = InetAddress.getLocalHost();
          }
          catch (UnknownHostException ex) {
              System.out.println(ex.toString());
          }
          String lHost = lHostAddress.getHostAddress();
          StringBuffer sb = new StringBuffer(lHost).append(":");
          return (sb.append(getLocalUniqueId()).toString());
      }
    }

    I've tested this code using a simple app and I've gotten the following result:

    Five (5) Primary Keys:
    PK1-> 200.187.206.20:1ec7c4:e15cd11a53:-8000
    PK2-> 200.187.206.20:1ec7c4:e15cd11a53:-7fff
    PK3-> 200.187.206.20:1ec7c4:e15cd11a53:-7ffe
    PK4-> 200.187.206.20:1ec7c4:e15cd11a53:-7ffd
    PK5-> 200.187.206.20:1ec7c4:e15cd11a53:-7ffc

    As you can see, the result changes when I run the test app again:

    Five (5) Primary Keys:
    PK1-> 200.187.206.20:1ec7c4:e15cd30f83:-8000
    PK2-> 200.187.206.20:1ec7c4:e15cd30f83:-7fff
    PK3-> 200.187.206.20:1ec7c4:e15cd30f83:-7ffe
    PK4-> 200.187.206.20:1ec7c4:e15cd30f83:-7ffd
    PK5-> 200.187.206.20:1ec7c4:e15cd30f83:-7ffc

    So it looks like it would be feasible to use this approach to generate PKs.

    Regards,

    Cris
  52. Entity Bean Primary Key Generator[ Go to top ]

    Hi.
    First of all, I'd like to point out that the initial pattern shown here is not EJB server compliant, not even when running on one EJB server, and not even with serializable transaction isolation level. I'm not reffering to the RMI unique IDs mechanism which is irrelevant to the pattern.
    The assumption that most EJB server will only load one instance per Entity object (row) is not true. Actually, allmost all (I don't know any that don't) servers will load multiple instances to handle concurrent transactions. The only other solution would be blocking the second transaction, which is generally deprecated.
    Assuming two EJBs are created, for two concurrent transactions there is no guarantee they will se each others changes. Actually, there is a guarantee of the exam opposite: they will not see each others changes.
    Now, as for serialziable transactions, they will only protect you on servers that use pessimistic concurrency control algorithms. Oracle, for example, doesn't. Therefore, one of the ejbStore methods is likely to fail, but the client won't even know it. The counter is returned before data is written. The only cross-server compliant way to assure this would be to force an exclusive lock, using, for example, the SELECT...FOR UPDATE clause on ejbLoad.
    I don't think that this is the right solution.
    Let's ignore the multiple ID domains for a moment, and assume there is only one PK generator. Are you trying to represent an object, or a service? I would say, service. The service is getting the next ID. The fact you got them using a DB row is regardless to the point. Modeling the interface for this specific solution (using a DB row) is incorrect encapsulation. So, first of all, I think a Session bean is more apropriate.
    As for the implementation, this is a simple transaction that you learn in any DB course. This transaction has to use exclusive locks anyway, because it's not read only.
    The getNextID() is easy to implement using standard JDBC by issuing a SELECT...FOR UPDATE clause, and then an UPDATE clause.
    As to performance, this implementation uses one simple transaction, and should be highly more efficient than the Entity bean approach.
    It does not require the use of serializable transactions, and performs no lock promotion so there's no fear of dead-lock.
    The session bean should also be stateless (unless other services are also provided by it), and is therefore very light.
    Distribution across multiple EJB server instances, or any other EJB container/server implementation option would not affect this session bean, while the entity bean suggestion would only work well on servers using commit option A (and the matching concurrent transaction management option), which is not supported in iAS, for example.
    Enhancing this approach to support multiple domains is straight-forward. I started off with one domain to clearly show that this is not a business object, but rather a service.


    Regards
    Gal
  53. Entity Bean Primary Key Generator[ Go to top ]

    Is this true...

    "Assuming two EJBs are created, for two concurrent transactions there is no guarantee they will se each others changes. Actually, there is a guarantee of the exam opposite: they will not see each others changes."

    If so, why? It would make sense to me that when the second Entity Bean is created that the data to initialize it is grabbed from the DB, not copied from another instance. When you grab this info you also increment the highid. If this is all done in a serializable transaction, I do not see a problem. From what I understand a serializable transaction guarantees all of the acid properties in the strictest fashion. If this is true then an exclusive lock would have to be held on the row being read and updated. I find it hard to believe that Oracle would not do this. Now the question is, does the container set the isolation level to serializable for container managed transactions i.e. Entity Bean transactions?

    I have another solution. My solution involves a Stateless Session Bean and an Entity Bean. The Entity Bean is used strictly for caching the id parts in memory, if your App Server supports this. The Entity Bean never actually hits the DB the Session Bean does. When you call NextID() on the Session Bean It grabs an IDParts Entity Bean instance. If the Entity Bean instance has a valid highid and lowid then use it, otherwise the Session starts a serializable transaction, grabs the next highid, and increments the highid counter. Next it stuffs this info into the Entity Bean to cache it. In the Entity Bean you need to make sure you set the highid to some invalid number when activate is called. I am assuming that activate is called whenever a new Entity Bean instance is created or when it is coming from the pool (in situations where the bean is not cached).

    The only problem I see that may occur is if an App Server makes a copy of a cached bean instead of pulling the info from the DB, to service concurrent clients. Anyone know if this happens?

    Anyway...that is my 2 cents.

    Justin
  54. Entity Bean Primary Key Generator[ Go to top ]

    Here is my implementation of the HIGH/LOW id generator.

    /**
     * By Weicong Wang (wweicong at yahoo dot com)
     *
     * Stateless session bean
     *
     */

    package ejb.util;

    import java.sql.*;
    import java.util.*;
    import java.text.*;

    import javax.ejb.*;
    import javax.naming.*;
    import javax.sql.*;
    import javax.transaction.*;

    class UniqueID
    {
    public final static int DEFAULT_HIGH_BITS = 64; // need 112 later
    public final static int DEFAULT_LOW_BITS = 16;
    public final static int DEFAULT_HIGH_START = 0;
    public final static int DEFAULT_LOW_START = 1;

    public final static int NEED_NEXT_HIGH = -1;

    public String m_name;
    public int m_highBits;
    public long m_high;

    public int m_lowBits;
    public int m_low;

    private String m_highStr;
    private long m_maxLow;
    private NumberFormat m_format = NumberFormat.getInstance();

    public UniqueID(String name) {
    this(name, DEFAULT_HIGH_BITS, DEFAULT_HIGH_START,
    DEFAULT_LOW_BITS);
    }

    public UniqueID(String name, int highBits, long high,
    int lowBits) {
    m_name = name;

    m_highBits = highBits;
    m_high = high;

    m_lowBits = lowBits;
    m_low = DEFAULT_LOW_START;

    m_maxLow = (1 < int lowDigits = String.valueOf(m_maxLow).length();

    m_highStr = String.valueOf(m_high);

    m_format.setMinimumIntegerDigits(lowDigits);
    m_format.setMaximumIntegerDigits(lowDigits);
    m_format.setGroupingUsed(false);
    }

    public synchronized int nextLow() {
    if (m_low < m_maxLow) {
    return m_low++;
    }

    return NEED_NEXT_HIGH;
    }

    public String toString(int low) {
    String lowStr = m_format.format(low);
    return m_highStr + lowStr;
    }

    public String toString() {
    return m_name;
    }
    }

    /**
     * must be stateless
     */
    public class UniqueIDGeneratorBean implements SessionBean
    {
    //==============================================================================
    // Constants
    //==============================================================================
    public final static boolean kDebug = true;

    //==============================================================================
    // Variables
    //==============================================================================
    private SessionContext m_ctx;

    private Hashtable m_map = new Hashtable();

    //==============================================================================
    // SessionBean Interface
    //==============================================================================
    public void ejbCreate()
    throws CreateException {
    }

    public void ejbRemove() {

    }

    public void ejbActivate() {

    }

    public void ejbPassivate() {

    }

    public void setSessionContext(SessionContext ctx) {
    m_ctx = ctx;
    }

    //==============================================================================
    // Remote Interface
    //==============================================================================
    public String nextID(String name) {
    while (true) {
    UniqueID id = (UniqueID)m_map.get(name);
    if (id == null) {
    id = load(name);
    }

    int low = id.nextLow();
    if (low != UniqueID.NEED_NEXT_HIGH) {
    return id.toString(low);
    }

    // for next loop
    m_map.remove(name);
    }
    }

    //==============================================================================
    // Private methods
    //==============================================================================
    /**
    * From weblogic:
    *
    * There is a problem in the Oracle 8 server that causes this error when
    * using a FOR UPDATE statement with AUTOCOMMIT turned on (which is the
    * default state when using JDBC). This is known to happen on Oracle 8.0
    * and 8.1 on Solaris and on Oracle 8.1 on Windows NT. If you turn
    * AUTOCOMMIT off, you will not receive this error.
    */
    private UniqueID load(String name) {
    UserTransaction ut = null;
    Connection con = null;
    UniqueID id = null;

    // casuing ORA-01002: fetch out of sequence
    String query =
    "SELECT HIGH_BITS, NEXT_HIGH, LOW_BITS FROM UNIQUE_ID_GEN WHERE NAME = '" +
    name + "' FOR UPDATE";
    if (kDebug == true) {
    System.err.println("SELECT = " + query);
    }
    try {
    con = getConnection();
    boolean isAutoCommit = con.getAutoCommit();
    con.setAutoCommit(false);

    Statement stmt = con.createStatement();

    // begin
    ut = m_ctx.getUserTransaction();
    ut.begin();

    ResultSet rs = stmt.executeQuery(query);
    if (rs.next()) {
    id = new UniqueID(name, rs.getInt(1), rs.getLong(2), rs.getInt(3));
    }
    rs.close();

    if (id != null) {
    String update =
    "UPDATE UNIQUE_ID_GEN SET NEXT_HIGH = " + (id.m_high + 1) +
    " WHERE NAME = '" + name + "'";

    if (kDebug == true) {
    System.err.println("UPDATE = " + update);
    }

    int count = stmt.executeUpdate(update);
    if (count != 1) {
    throw new EJBException("Could not update UNIQUE_ID_GEN");
    }
    } else {
    id = new UniqueID(name);

    // insert default
    StringBuffer sbInsert = new StringBuffer();
    sbInsert.append("INSERT INTO UNIQUE_ID_GEN (NAME, HIGH_BITS, NEXT_HIGH, LOW_BITS) VALUES ('");
    sbInsert.append(id.m_name).append("', ").append(id.m_highBits).append(", ");
    sbInsert.append(id.m_high).append(", ").append(id.m_lowBits).append(")");

    if (kDebug == true) {
    System.err.println("INSERT = " + sbInsert);
    }

    int count = stmt.executeUpdate(sbInsert.toString());
    if (count != 1) {
    throw new EJBException("Could not insert UNIQUE_ID_GEN");
    }
    }

    // commit explictly
    con.commit();

    // done
    ut.commit();

    stmt.close();
    con.setAutoCommit(isAutoCommit);

    m_map.put(id.m_name, id);

    return id;
    } catch (Exception e) {
    e.printStackTrace();
    if (ut != null) {
    try {
    ut.rollback();
    } catch (Exception ee) {
    ee.printStackTrace();
    }
    }
    throw new EJBException(e);
    } finally {
    closeConnection(con);
    }
    }

            ...
    }
  55. Hello everyone,

    We have studied the thread on ID generation very carefully. There are many good ideas. I wanted to share our solution and see if anyone has a suggestion to a small problem we are having.

    We decided to implement the high low method. However, instead of issuing the ids form an EJB we implemented a singleton class that manages this service. We have a similar pattern where we serve properties via a publish subscription model that works very well.

    Everything seemed to work fine, however, the GUIDGen class requests a JDBC connection from WebSphere as Data service and attempts to change the isolation level to serializable. It seems that JDBC connection served is the same connection served from the outlaying EJBs and the transaction started in the context. As soon as we attempt to change the isolation level a &#8220;Can not change isolation level within a transaction&#8221; error ith thrown.

    The behavior we wish to have is the JDBC connection being served from the WebSphere to be a new connection with its own transaction and isolation level. Does anyone know how to force this behavior and still be able to use a connection pool?

    Any help or additional explanation would be appreciated.

    Russell Georgen
  56. Entity Bean Primary Key Generator[ Go to top ]

    Mark the components as TX_NOT_SUPPORTED

    Secondly, I would strongly urge you to avoid singletons. They are note very portable.

    Dave Wolf
    Internet Applications Division
    Sybase
  57. Entity Bean Primary Key Generator[ Go to top ]

    How about this implementation:
    http://www.mail-archive.com/orion-interest at orionserver dot com/msg01383.html

    I would like to know to 'final' solution, the 'best' solution. The one that works.

    Thanks,
    Mathias
  58. Mathias, ALL,

    Funny you say that. I was just compiling something. I had a look at your link and will make a few comments this week I hope. For the time beeing here is what I worked on.

    I have been studying this thread since I first found it two weeks ago.

    As a result I made an attempt to synthesize what has been said so far. Since the problem is a fairly basic and general one I decided to write code that would completely implement the solution that I thought was the one emerging from this discussion thread. I will make the code freely available on the SourceForge as soon as I have got acceptance from tht Source Forge team (I will update you when it is set up, watch for the ejbutils project), hoping that it would take advantage of the open source community and offer a quality and stable solution for everyone. It would be great if you could use it, improve it, and test it on your respective platforms. I hope/think the initial quality is up to it (well I made some late changes without an IDE, BUT I will keep you updated as soon as I am sure it is fine): I wrote it over a weekend (do not worry, my life is not that sad...). I haven't had time to test it on WebSphere 3.5, which I am working on currently, but by the time you read this article, it will probably have been done...

    Please, read Scott Ambler's article ( here) BEFORE making any comments/contributions to this posting. References to postings from this thread will be made, with links directing you to them.

    The code implements the following aspects discussed here:

    - Uses a key composed of a 112 bits HIGH key, a 16 bits LOW key and a unique enterprise identifier (as per Scott Ambler's article).

    - Stores the key as a String in the database (as per Scott Ambler's article).

    - Supposes that all classes will use the same generic generation mechanism to obtain UIDs for their objects (as per Scott Ambler's article).

    - Creates the HIGH key automatically in the database if it is not found.

    - Uses singleton/factory pattern as discussed by Serge Monkewitz (August 8, 2000). This means that there will be one factory per JVM.
    [ NOTE: As mentioned in various postings, some EJB servers, such a SilverStream and Gemstone, are creating and destroying JVMs dynamically. I took the same position as Scott Amblers when he says in his article: "Yes, this is wasteful, but when you are dealing with a 112-bit HIGHs, who cares?" If this is really an issue, decisions might need to be taken at the level of such servers' configuration, but this is a separate discussion all together.]

    - Uses a byte array to represent the HIGH and LOW keys. The byte arrays are encapsulated in a Key class that implements various functionalities such as incrementing the key, converting back and forth to String, etc. I think this solution is acceptable performance wise although I did not study the problem thoroughly.

    - In a first try (e.g. just an idea out of the blue) to solve the "hotspot" problem in index pages updates mentioned by Eddie Fung (July 30, 2000), I used the strategy of incrementing the keys (byte arrays) in inverse order.

    This means that instead of incrementing like this:

    00000000 00000000 ... 00000000 00000000 -> 00000000 00000000 ... 00000000 00000001
    and:
    00000000 00000000 ... 00000000 11111111 -> 00000000 00000000 ... 00000001 00000000

    the ByteArray class would increment the following way:

    00000000 00000000 ... 00000000 00000000 -> 00000001 00000000 ... 00000000 00000000
    and:
    00000000 11111111 ... 00000000 00000000 -> 00000000 00000001 ... 00000000 00000000

    This means that instead of waiting for the end of the byte array to see any difference between HIGH keys, the difference will appear at the beginning (especially at early stages of the life of the UID generator).

    - Uses a Session bean with a "select for update" to get the HIGH key as discussed by Gal Binyamini (September 3, 2000) . According to Gal (please anybody, shout if there is some disagreement here), the resulting bean does not require the use of a TX_SERIALIZEABLE transaction, BUT you should use TX_REQUIRES_NEW to make the bean highly available.

    - Makes sure the AUTOCOMMIT is turn off and then back to its initial setting after the comment from Weicong Wang (September 28, 2000) .

    Cheers,

    Emmanuel

    ehsforward at yahoo dot com


  59. Ooops,

    Just realised I made a typo.

    You should read:

    This means that instead of incrementing like this:

    00000000 00000000 ... 00000000 00000000 -> 00000000 00000000 ... 00000000 00000001
    and:
    00000000 00000000 ... 00000000 11111111 -> 00000000 00000000 ... 00000001 00000000

    the ByteArray class would increment the following way:

    00000000 00000000 ... 00000000 00000000 -> 00000001 00000000 ... 00000000 00000000
    and:
    11111111 00000000 ... 00000000 00000000 -> 00000000 00000001 ... 00000000 00000000
    (ans not 00000000 11111111 ... 00000000 00000000 -> 00000000 00000001 ... 00000000 00000000)

    Cheers

    Emmanuel
  60. Emmanuel,

       Your idea is excellent! I really think we have hit on something big here, a perfectly portable primary key generation strategy which is ultra high performing! I encourage you to take up the source forge project, but first I encourage you to repost this "new" strategy as a separate pattern. The HIGH/LOW EJB generator pattern is outside the scope of this original thread, and should be posted in its own pattern to get the right attention (and to keep this thread from becoming gargantuan). :)

       Please include an intro into the motivation behind this pattern and a description of it (so that the new pattern can be read independantly from this one).

    Floyd

        
  61. All,

    Please note that I have created a new discussion (which should have been a pattern) here where you will be able to find the code.

    Emmanuel
  62. i've gone through your code -- it looks complete and well thought out. i do have a question regarding your use of a session bean to encapsulate the JDBC call to fetch the next HIGH value.

    it seems to me that this is just adding an unneccesary layer of indirection (going through the container). first of all, you can guarantee that there is a only one singleton factory generator per jvm (your singleton/factory). since the getNextID method is synchronized, there will only be a single database call at a time to fetch the next high id (findNewHighKey is wrapped in the synchronized method).

    that being said, why not simply put the jdbc code in the UIDHighKeyGenerator session bean right into the findNewHighKey method? it's guaranteed to be serialized per jvm based upon the synchronized method. you reference a post by gal binyamini as part of your rationale for using a session bean (i think). i think he's only suggesting it as an alternative to the entity bean originally suggested, however.
  63. Sorry for the late reply Justin,

    The discussion had moved to another thread.

    Anyway, to answer your first paragraphe: yes that's fine as long as you have only have one jvm. In a distributed environment, you have several server, several jvms and you cannot take the singleton approach. This is not scalable.

    I guess this answers you second paragraph too.

    Emmanuel
  64. If you want a working, ready-to-use solution which considers the issues mentioned in the thread you can try the UID Generator provided by X-Steps, a free download is available.

    Look at http://www.xsteps.com/english/products/ejbs/uidGenerator.shtml

    Greetings,

    Messi
  65. The link here is out of fuction!
  66. Entity Bean Primary Key Generator[ Go to top ]

    When I try to compile the programe the compiler generate an error message that says there is an error in line 413