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

    EmbeddedId and GeneratedValue

    nholbrook

      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

          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.

          • 2. Re: EmbeddedId and GeneratedValue
            nholbrook
            • 3. Re: EmbeddedId and GeneratedValue
              nholbrook

              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
                squishy

                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
                  epbernard

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

                  • 6. Re: EmbeddedId and GeneratedValue
                    squishy

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

                    • 7. Re: EmbeddedId and GeneratedValue
                      epbernard

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

                      • 8. Re: EmbeddedId and GeneratedValue
                        squishy

                        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
                          squishy

                          (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
                            epbernard

                            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
                              squishy

                              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
                                squishy

                                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
                                  squishy

                                  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
                                    squishy

                                    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