1 2 Previous Next 16 Replies Latest reply on Jun 8, 2006 6:52 AM by Holger Johanndeiter

    EmbeddedId and GeneratedValue

    Nic Holbrook Newbie

      I have a composite key with 2 fields. 1 field is an auto-incrementing field, the other is a foreign key. I've been trying to put @GeneratedValue annotation on the auto-incrementing field, but can't seem to get it to work properly. Is this supported? I assumed thats why it was broken out of the @ID annotation so that it could be used on any field.

      This is a major setback to our development if this isn't supported.

      ----- Customer Class -----
      @EmbeddedId
      @AttributeOverrides( {
      @AttributeOverride(name = "customerId", column = @Column(name = "customer_id", unique = false, nullable = false, insertable = true, updatable = true)),
      @AttributeOverride(name = "carrierId", column = @Column(name = "carrier_id", unique = false, nullable = false, insertable = true, updatable = true))})
      public CustomerId getId()
      {
      return this.id;
      }

      ------- CustomerId class ---------------

      @GeneratedValue(strategy= GenerationType.AUTO)
      @Column(name = "customer_id", unique = true, nullable = false, insertable = true, updatable = true)
      public Integer getCustomerId()
      {
      return this.customerId;
      }

      @Column(name = "carrier_id", unique = false, nullable = false, insertable = true, updatable = true)
      public Integer getCarrierId()
      {
      return this.carrierId;
      }

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

      Thanks

      Nic

        • 1. Re: EmbeddedId and GeneratedValue
          Bill Burke Master

          id generation is not supported/required by the specification for composite keys.

          I do suggest you log a feature request and bug on www.hibernate.org

          feature request: to allow generated values for composite id properties
          bug report: to get a clearer error message.

          • 3. Re: EmbeddedId and GeneratedValue
            Nic Holbrook Newbie

            Well, I did some digging around and came up with an interim solution that seems to work pretty well. I have multiple databases so I picked my common module to manage autogenerated id's for compount primary keys. The first is the entity object I generated in the database. The second is the IDGenerator class for my composite-id entity objects. The third is the basic logic behind the generator. Anyway, I just thought I'd post this since there isn't a lot of information out there on GenericGenerators. If I have something bad in here, let me know...

            Thanks

            Nic

            ------------------------------------- Entity on the table that manages the id's -----------------------------

            @Entity
            @Table(name="sec_uuid")
            public class SecUUID implements java.io.Serializable
            {
            private String entity;
            private Long value;

            public SecUUID()
            {

            }

            public SecUUID(String entity, Long value)
            {
            this.entity = entity;
            this.value = value;
            }

            @Id
            @Column(name="entity", unique = true, nullable = false, insertable = true, updatable = true, length = 45)
            public String getEntity()
            {
            return entity;
            }
            public void setEntity(String entity)
            {
            this.entity = entity;
            }

            @Column(name="value", unique = false, nullable = true, insertable = true, updatable = true)
            public Long getValue()
            {
            return value;
            }
            public void setValue(Long value)
            {
            this.value = value;
            }
            }

            --------------------------------------- Id generator class -------------------------------------------

            /**
            *
            * The following is an example annotation to be used on the class declaration of the entity.
            * Column name(In this case customerId) is the name of the property inside the embedded id that needs to be auto-incremented.
            * The column also needs to be of type Long.
            * @GenericGenerator(name = "KeyGenerator", strategy = "EmbeddedIDGenerator",
            * parameters = {
            * @Parameter (name=EmbeddedIDGenerator.COLUMN_NAME, value="customerId")
            * }
            * )
            *
            *
            * The following is the tag that needs to put on the Id field of the entity. This field must
            * be an embedded id and the name must be id.
            * @GeneratedValue(generator = "KeyGenerator")
            */
            public class EmbeddedIDGenerator
            implements IdentifierGenerator, Configurable
            {
            private static final Log log = LogFactory.getLog(EmbeddedIDGenerator.class);
            public static final String COLUMN_NAME = "uuid_column";

            private String entityName;
            private String columnName;

            public Serializable generate(SessionImplementor sessionImplementor, Object arg1)
            throws HibernateException
            {
            log.debug("Generating id for entity " + entityName);

            Object entity = arg1;

            try
            {
            log.debug("Working on entity " + entity.getClass().getName());
            Serializable key = (Serializable)PropertyUtils.getProperty(entity, "id");
            log.debug("I have key for entity " + key.getClass().getName());
            log.debug("Looking up the next uuid for key " + key);

            // This is where you will make a call or execute sql to get your next id.
            Long id = ProxyFactory.getProxy().getNextUUID(entityName);


            log.debug("Setting the value " + id + " on property " + columnName);
            PropertyUtils.setProperty(key, columnName, id);

            return key;
            }
            catch (IllegalAccessException e)
            {
            log.error(e);
            throw new TessRuntimeException("There was an error generating the id for " + entityName);
            }
            catch (InvocationTargetException e)
            {
            log.error(e);
            throw new TessRuntimeException("There was an error generating the id for " + entityName + ". Key must have a setter for column " + columnName);
            }
            catch (NoSuchMethodException e)
            {
            log.error(e);
            throw new TessRuntimeException("There was an error generating the id for " + entityName + ". Entity must have a getId() method.");
            }
            }

            public void configure(Type type, Properties properties, Dialect dialect)
            throws MappingException
            {
            entityName = properties.getProperty(ENTITY_NAME);
            columnName = properties.getProperty(COLUMN_NAME);
            }
            }



            ---------------------------------------------------------- UUID Generation Logic --------------

            public Long getNextUUID(String entityName)
            {

            String QUERY = "from SecUUID as uuid where uuid.entity = :entityName";
            SecUUID uuid;
            try
            {
            log.debug("Looking up entity " + entityName);
            uuid = (SecUUID)pm.getEntityManager().createQuery(QUERY).setParameter("entityName", entityName).getSingleResult();
            uuid.setValue(uuid.getValue() + 1L);
            log.debug("Incrementing the uuid by 1");
            log.debug("updating value to " + uuid.getValue());
            pm.getEntityManager().flush();
            return uuid.getValue();
            }
            catch (NoResultException e)
            {
            log.debug("No uuid fond for entity " + entityName);
            pm.insert(new SecUUID(entityName, 1L));
            log.debug("Inserted entry for " + entityName);
            return 1L;
            }
            }

            -------------------------------- Example Entity -------------------------------------

            @Entity
            @GenericGenerator(name = "KeyGenerator", strategy = "com.bla.util.EmbeddedIDGenerator",
            parameters = {
            @Parameter (name=EmbeddedIDGenerator.COLUMN_NAME, value="customerId")
            })
            @Table(name = "customer", catalog = "tescsm", uniqueConstraints = {})
            public class Customer
            {
            @EmbeddedId
            @GeneratedValue(generator = "KeyGenerator")
            @AttributeOverrides( {
            @AttributeOverride(name = "customerId", column = @Column(name = "customer_id", unique = false, nullable = false, insertable = true, updatable = true)),
            @AttributeOverride(name = "carrierId", column = @Column(name = "carrier_id", unique = false, nullable = false, insertable = true, updatable = true))})
            public CustomerId getId()
            {
            return this.id;
            }
            }

            -------------------------------------- Embeddable Id ------------------------------

            @Embeddable
            public class CarrierCustomerId
            implements java.io.Serializable
            {

            // Fields

            private Long customerId;
            private Integer carrierId;

            // Constructors

            /** default constructor */
            public CarrierCustomerId()
            {
            }

            /** full constructor */
            public CarrierCustomerId(Long customerId, Integer carrierId)
            {
            this.customerId = customerId;
            this.carrierId = carrierId;
            }

            // Property accessors

            @Column(name = "customer_id", unique = true, nullable = false, insertable = true, updatable = true)
            @GeneratedValue
            public Long getCustomerId()
            {
            return this.customerId;
            }

            public void setCustomerId(Long customerId)
            {
            this.customerId = customerId;
            }

            @Column(name = "carrier_id", unique = false, nullable = false, insertable = true, updatable = true)
            public Integer getCarrierId()
            {
            return this.carrierId;
            }

            public void setCarrierId(Integer carrierId)
            {
            this.carrierId = carrierId;
            }
            }

            • 4. Re: EmbeddedId and GeneratedValue
              Holger Johanndeiter Newbie

              I think it's surprising that there's so little about composite primary keys with auto generated values yet..

              Isn't it also possible that, evertime you add a new entry to the database you do a query like (Role is the entity i tried with)

              "SELECT MAX(r.roleId) + 1 FROM Role r"
              

              and take it as the ID?
              I don't like it this way, because the code isnt in the entity itself, but i prefer it over creating a whole new table for primary keys, which doesn't appear very clean to me...

              Isn't there a proper implementation for @GeneratedValue by the container/hibernate yet??

              • 5. Re: EmbeddedId and GeneratedValue
                Emmanuel Bernard Master

                your solution can be very inefficient and conceptually does not work. you need to think about concurrent transactions.

                • 6. Re: EmbeddedId and GeneratedValue
                  Holger Johanndeiter Newbie

                  i thoguht so. do you have an idea for working solution? i'd be very thankful :)

                  • 7. Re: EmbeddedId and GeneratedValue
                    Emmanuel Bernard Master

                    the one provided by the spec :-)
                    TABLE is very efficient, clean and respectful of your Tx concurrency.

                    • 8. Re: EmbeddedId and GeneratedValue
                      Holger Johanndeiter Newbie

                      uh, there is one? tried several things with @GeneratedValue Annotation, but the only id i got generated was 0 :o

                      • 9. Re: EmbeddedId and GeneratedValue
                        Holger Johanndeiter Newbie

                        (the class has @IdClass with appropriate PK-class)

                        @Id @GeneratedValue(strategy=GenerationType.TABLE)
                         public int getUserId() {
                         return userId;
                         }
                        

                        leads to a
                        java.sql.BatchUpdateException: failed batch

                        on the 2nd insert into the database.
                        1 Entity is wrote to the database, its Id is 0.

                        is this how it is supposed to be? :o




                        • 10. Re: EmbeddedId and GeneratedValue
                          Emmanuel Bernard Master

                          check the test suites this definitely works.
                          If you don't use DDL generation, you'll have to create the increment table yourself before running the app

                          • 11. Re: EmbeddedId and GeneratedValue
                            Holger Johanndeiter Newbie

                            apparently hibernate does not create an entry in HIBERNATE_SEQUENCES for my Bean with the PK class, but for the same one without pk class..
                            Uhm.. can you give me a hint on how to find a test suit that tests this.. ? (no clue of them test suits :o)

                            • 12. Re: EmbeddedId and GeneratedValue
                              Holger Johanndeiter Newbie

                              I tested the same beans with new Jboss 4.0.4CR2 + EJB3CR6, the only thing that's different is that i get no errors, because the row with id 0 is simply overridden by each new entity that is added to the database. (no errors there!)
                              No HIBERNATE_SEQUENCES table is created either.

                              Could you be so kind to show me a working example? (or point me to where i find one :)
                              im totally lost here..

                              • 13. Re: EmbeddedId and GeneratedValue
                                Holger Johanndeiter Newbie

                                just realized the overiding happens because i used manager.merge instead of manager.persist .
                                Doesn't solve my problem though..

                                • 14. Re: EmbeddedId and GeneratedValue
                                  Holger Johanndeiter Newbie

                                  Entity Class:

                                  package ejbframe.entity;
                                  
                                  import javax.persistence.*;
                                  
                                  @Entity
                                  @IdClass(UserPK.class)
                                  @Table(name = "users")
                                  public class User {
                                  
                                   private int userId;
                                   private String name;
                                   private String password;
                                  
                                   @Id @GeneratedValue(strategy = GenerationType.TABLE)
                                   public int getUserId() {
                                   return userId;
                                   }
                                   public void setUserId(int userId) {
                                   this.userId = userId;
                                   }
                                  
                                   public String getName() {
                                   return name;
                                   }
                                   public void setName(String name) {
                                   this.name = name;
                                   }
                                  
                                   public String getPassword() {
                                   return password;
                                   }
                                   public void setPassword(String password) {
                                   this.password = password;
                                   }
                                  }
                                  


                                  PK class:

                                  package ejbframe.entity;
                                  
                                  import java.io.Serializable;
                                  
                                  public class UserPK implements Serializable {
                                  
                                   private int userId;
                                  
                                   public UserPK () {}
                                  
                                  
                                   public int getUserId() {
                                   return userId;
                                   }
                                  
                                   public void setUserId(int userId) {
                                   this.userId = userId;
                                   }
                                  }
                                  


                                  no ids are generated, no tables for id generation created.
                                  If i comment out the @IdClass it works perfectly.
                                  help please :(

                                  1 2 Previous Next