-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: @EmbeddedId that contains an Identity column on SQL Server
PostPosted: Thu Oct 08, 2009 3:08 am 
Newbie

Joined: Wed Feb 18, 2009 5:03 pm
Posts: 5
I'm working on a schema requiring a composite primary key, something like:

Code:
@Entity
public class Event {
  @EmbeddedId
  private EventKey id;
}

@Embeddable
public class EventKey {
  @GeneratedValue
  public Long id;

  @Column(nullable=false)
  @Temporal(TemporalType.TIMESTAMP)   
  public Date timestamp;
}


I want "id" in the composite key to be the IDENTITY column on the table, but that doesn't work.

As shown above, the embedded id does not get treated like a generated value. DDL generation does not mark the table as an IDENTITY column at all.

If you change it to this:
Code:
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  @Generated(GenerationTime.INSERT)
  @Column(columnDefinition="BIGINT IDENTITY(1,1)")
  private Long id;

... then hbm2dll creates an Identity column (as it's explicitly there), but Hibernate doesn't recognise it. It tries to insert "NULL" as an explicit value, which is illegal.

If you do this:
Code:
@Id
@GeneratedValue
public Long id;

Then you get "EventKey must not have @Id properties when used as an @EmbeddedId".

Is there any way to make this work? I would have thought having @GeneratedValue inside an @EmbeddedId would infer that this was the IDENTITY column. Do I perhaps need to use @IdClass instead, and specify the @Id fields separately on every Entity class?

Any/all help appreciated. :)


Top
 Profile  
 
 Post subject: Re: @EmbeddedId that contains an Identity column on SQL Server
PostPosted: Thu Oct 08, 2009 7:01 pm 
Newbie

Joined: Wed Feb 18, 2009 5:03 pm
Posts: 5
@IdClass doesn't seem to work either, gives the same results as described above.


Top
 Profile  
 
 Post subject: Re: @EmbeddedId that contains an Identity column on SQL Server
PostPosted: Thu Oct 08, 2009 8:14 pm 
Newbie

Joined: Wed Feb 18, 2009 5:03 pm
Posts: 5
OK, further Googling has revealed that this won't work - you can't combine EmbeddedId with a GeneratedValue.

Hibernate forum post where it's stated plainly:
https://forums.hibernate.org/viewtopic. ... 130987bc22

Some discussion about this limitation in the spec;
http://www.jboss.org/index.html?module= ... &p=3928306

and a related feature request. Apparently the workaround is to use UserType, so I'm off to read up on that now:
http://opensource.atlassian.com/project ... se/ANN-268


Top
 Profile  
 
 Post subject: Re: @EmbeddedId that contains an Identity column on SQL Server
PostPosted: Tue Mar 30, 2010 4:36 pm 
Newbie

Joined: Tue Jan 11, 2005 6:28 pm
Posts: 3
This Oct 2009 thread included the suggestion that a workaround would involve a UserType implementation. I implemented UserType to deal with enumerations back in Hibernate 2 days, but it is not clear to me how a UserType would help implement a composite PK especially one that includes a sequence column.

Can anyone provide an overview of how that would work?


Top
 Profile  
 
 Post subject: Re: @EmbeddedId that contains an Identity column on SQL Server
PostPosted: Thu Oct 07, 2010 10:20 am 
Newbie

Joined: Thu Oct 07, 2010 10:16 am
Posts: 4
up

I have the same question


Top
 Profile  
 
 Post subject: Re: @EmbeddedId that contains an Identity column on SQL Server
PostPosted: Fri Oct 19, 2012 7:43 am 
Newbie

Joined: Fri Oct 19, 2012 7:37 am
Posts: 1
I've got trapped in the same problem and decided to share my solution.

I build my own EmbeddedSequenceGenerator which will, when well configured, allow to set a generated value in an embeddedId.
Use case is very simple but it worked for me.

Hope it'll help :

Code:
package org.hibernate.id;

import java.io.Serializable;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.PrimitiveType;
import org.hibernate.type.Type;

/*
* Sequence generator for specified field in EmbeddedId.<br />
*
* This Generator needs multiple properties to be set :
* <ul><li>className : the class name of the entity persisted</li>
*     <li>idGetterMethod : the getterMethod name for the id</li>
*     <li>idClassName : the class name of the EmbeddedId</li>
*     <li>idClassHibernateType : the class name of the Generated Value by Hibernate</li>
*     <li>generatedFieldGetterMethod : the getter method on the id for the generated value</li>
*     <li>generatedFieldSetterMethod : the setter method on the id for the generated value</li>
* </ul>
*
* Example of use :
* &#64;GenericGenerator(
*   name = "OPERATION_ID_SEQ",
*   strategy = "fr.sfr.sivpn.persistance.sequence.EmbeddedSequenceGenerator",
*   parameters =
*   {
*     &#64;Parameter(name = "sequence", value = "ID_RTE_SEQ"),
*     &#64;Parameter(name = "className", value = "fr.sfr.sivpn.persistance.entity.engine.OperationEntity"),
*     &#64;Parameter(name = "idGetterMethod", value = "getId"),
*     &#64;Parameter(name = "idClassName", value = "fr.sfr.sivpn.persistance.entity.ids.OperationId"),
*     &#64;Parameter(name = "generatedFieldGetterMethod", value = "getIdOp"),
*     &#64;Parameter(name = "idClassHibernateType", value = "org.hibernate.type.LongType"),
*     &#64;Parameter(name = "generatedFieldSetterMethod", value = "setIdOp")
*   })
*
* @author lschoelens
* @version 1.0
* @since 1.0 (2012/10/19)
*/
public class EmbeddedSequenceGenerator extends SequenceGenerator
{
   /** Entity class param name. */
   public static final String CLASS_NAME = "className";
   
   /** Paramètre pour définir le getter de l'id sur l'entité. */
   public static final String ID_GETTER_METHOD = "idGetterMethod";
   
   /** Paramètre pour définir la classe de l'id de l'entité. */
   public static final String ID_CLASS_NAME = "idClassName";
   
   /** Paramètre pour définir la classe hibernate de l'id de l'entité. */
   public static final String ID_CLASS_HIBERNATE_TYPE = "idClassHibernateType";
   
   /** Paramètre pour définir le getter du champ généré. */
   public static final String ID_GETTER_GENERATED_FIELD = "generatedFieldGetterMethod";
   
   /** Paramètre pour définir le setter du champ généré. */
   public static final String ID_SETTER_GENERATED_FIELD = "generatedFieldSetterMethod";
   
   /** La classe de l'entité. */
   private Class<?> clazz;
   
   /** La méthode de récupération de l'id. */
   private java.lang.reflect.Method getterMethod;
   
   /** La classe de l'entité. */
   private Class<?> idClazz;
   
   /** La classe hibernate de l'id généré au sein de l'id. */
   private Type idType;
   
   /** La méthode de récupération de la valeur à générer sur l'id. */
   private java.lang.reflect.Method getterGeneratedValueMethod;
   
   /** La méthode de récupération de la valeur à setter sur l'id. */
   private java.lang.reflect.Method setterGeneratedValueMethod;

   /*
    * Configure the super generator with right HibernateType for the generated value.<br />
    * Then configure methods access and class names for the generation process.
    *
    * TODO : add a way to access property directly.
    * @see org.hibernate.id.SequenceGenerator#configure(
    *    org.hibernate.type.Type, java.util.Properties, org.hibernate.dialect.Dialect)
    */
   @Override
   public void configure(Type type, Properties params, Dialect dialect)
      throws MappingException
   {
      final String idTypeName = params.getProperty(ID_CLASS_HIBERNATE_TYPE);
      
      if (idTypeName == null)
      {
         throw new IllegalStateException(ID_CLASS_HIBERNATE_TYPE + " property cannot be null");
      }
      
      try
      {
         Class<?> clazzType = Class.forName(idTypeName);
         idType = (Type) clazzType.newInstance();
      }
      catch (ClassNotFoundException e)
      {
         throw new IllegalStateException(ID_CLASS_HIBERNATE_TYPE + " is unknown class", e);
      }
      catch (ClassCastException e)
      {
         throw new IllegalStateException(ID_CLASS_HIBERNATE_TYPE + " is not a Type class", e);
      }
      catch (InstantiationException e)
      {
         throw new IllegalStateException(ID_CLASS_HIBERNATE_TYPE + " cannot be instantiated", e);
      }
      catch (IllegalAccessException e)
      {
         throw new IllegalStateException(ID_CLASS_HIBERNATE_TYPE + " cannot be accessed", e);
      }
      
      super.configure(idType, params, dialect);
      
      initFields(params);
   }

   /**
    * Init method for fields.
    * @param params - Parameters in annotation.
    */
   private void initFields(Properties params)
   {
      final String className = params.getProperty(CLASS_NAME);
      
      if (className == null)
      {
         throw new IllegalStateException(CLASS_NAME + " property cannot be null");
      }
      
      try
      {
         clazz = Class.forName(className);
      }
      catch (ClassNotFoundException e)
      {
         throw new IllegalStateException(CLASS_NAME + " is unknown class", e);
      }
      
      final String idGetterMethod = params.getProperty(ID_GETTER_METHOD);
      
      if (idGetterMethod == null)
      {
         throw new IllegalStateException(ID_GETTER_METHOD + " property cannot be null");
      }
      
      try
      {
         getterMethod = clazz.getDeclaredMethod(idGetterMethod);
      }
      catch (SecurityException e)
      {
         throw new IllegalStateException(ID_GETTER_METHOD + " cannot be access : security violation", e);
      }
      catch (NoSuchMethodException e)
      {
         throw new IllegalStateException(ID_GETTER_METHOD + " does not exists", e);
      }
      
      final String classNameId = params.getProperty(ID_CLASS_NAME);
      
      if (classNameId == null)
      {
         throw new IllegalStateException(ID_CLASS_NAME + " property cannot be null");
      }
      
      try
      {
         idClazz = Class.forName(classNameId);
      }
      catch (ClassNotFoundException e)
      {
         throw new IllegalStateException(ID_CLASS_NAME + " is unknown class", e);
      }
      
      Class<?> idClazzValue = idType.getReturnedClass();
      
      final String idGeneratedValueGetterMethod = params.getProperty(ID_GETTER_GENERATED_FIELD);
      
      if (idGeneratedValueGetterMethod == null)
      {
         throw new IllegalStateException(ID_GETTER_GENERATED_FIELD + " property cannot be null");
      }
      
      try
      {
         getterGeneratedValueMethod = idClazz.getDeclaredMethod(idGeneratedValueGetterMethod);
      }
      catch (SecurityException e)
      {
         throw new IllegalStateException(ID_GETTER_GENERATED_FIELD + " cannot be access : security violation", e);
      }
      catch (NoSuchMethodException e)
      {
         throw new IllegalStateException(ID_GETTER_GENERATED_FIELD + " does not exists", e);
      }
      
      final String idGeneratedValueSetterMethod = params.getProperty(ID_SETTER_GENERATED_FIELD);
      
      if (idGeneratedValueSetterMethod == null)
      {
         throw new IllegalStateException(ID_SETTER_GENERATED_FIELD + " property cannot be null");
      }
      
      try
      {
         setterGeneratedValueMethod = idClazz.getDeclaredMethod(idGeneratedValueSetterMethod, idClazzValue);
      }
      catch (SecurityException e)
      {
         throw new IllegalStateException(ID_SETTER_GENERATED_FIELD + " cannot be access : security violation", e);
      }
      catch (NoSuchMethodException e)
      {
         throw new IllegalStateException(ID_SETTER_GENERATED_FIELD + " does not exists", e);
      }
   }

   /*
    * Access the id of the current entity and generate value if and only if generated value to be set is null.
    *
    * TODO Direct access to id field and value field
    * @see org.hibernate.id.SequenceGenerator#generate(org.hibernate.engine.SessionImplementor, java.lang.Object)
    */
   @Override
   public synchronized Serializable generate(SessionImplementor session, Object obj)
      throws HibernateException
   {
      if (obj.getClass().equals(clazz))
      {
         try
         {
            Serializable idObj = (Serializable) getterMethod.invoke(obj);
            if (idObj == null)
            {
               idObj = (Serializable) idClazz.newInstance();
            }
            
            if (idObj.getClass().equals(idClazz))
            {
               Object id = getterGeneratedValueMethod.invoke(idObj);
               if (isNullOrDefault(id))
               {
                  id = super.generate(session, obj);
                  setterGeneratedValueMethod.invoke(idObj, id);
               }
            }
            
            return idObj;
         }
         catch (IllegalArgumentException e)
         {
            throw new HibernateException(e);
         }
         catch (IllegalAccessException e)
         {
            throw new HibernateException(e);
         }
         catch (java.lang.reflect.InvocationTargetException e)
         {
            throw new HibernateException(e);
         }
         catch (InstantiationException e)
         {
            throw new HibernateException(e);
         }
      }

      return super.generate(session, obj);
   }

   /**
    * Check if the id is null or default in case of PrimitiveType.
    * @param id - the id
    * @return {@link Boolean} - <code>true</code> in case idType is <code>null</code> or default value
    */
   private boolean isNullOrDefault(Object id)
   {
      if (idType instanceof PrimitiveType<?>)
      {
         return id == null || ((PrimitiveType<?>) idType).getDefaultValue().equals(id);
      }
      
      return id == null;
   }
}


With an example :

Entity class :

Code:
@Entity
@Table(name = "MY_TABLE")
@GenericGenerator(name = "MY_SEQ",
   strategy = "org.hibernate.id.EmbeddedSequenceGenerator",
   parameters =
   {
      @Parameter(name = "sequence", value = "MY_SEQ_NAME"),
      @Parameter(name = "className", value = "my.package.OperationEntity"),
      @Parameter(name = "idGetterMethod", value = "getId"),
      @Parameter(name = "idClassName", value = "my.package.OperationId"),
      @Parameter(name = "generatedFieldGetterMethod", value = "getIdOp"),
      @Parameter(name = "idClassHibernateType", value = "org.hibernate.type.LongType"),
      @Parameter(name = "generatedFieldSetterMethod", value = "setIdOp")
   })
public class OperationEntity implements Serializable
{
   /** Id. */
   private OperationId id;
   
   /**
    * @return {@link OperationId} - the id
    */
   @EmbeddedId
   @GeneratedValue(generator = "OPERATION_ID_SEQ")
   public synchronized OperationId getId()
   {
      // Create new if null to avoid NPE erro
      if (id == null)
      {
         id = new OperationId();
      }
      
      return id;
   }

   /**
    * @param id - the id
    */
   public void setId(OperationId id)
   {
      this.id = id;
   }
   
   // Other properties
}


Id class :
Code:
/**
* My EmbeddedId for OperationEntity class.
*/
@Embeddable
public class OperationId implements Serializable
{
   private static final long serialVersionUID = -7269582911420970707L;
   
   /** Generated id with sequence. */
   private Long idOp;
   /** Other field setted by user. */
   private Date sendDate;
   
   public OperationId()
   {
      // Default : no id
      this(null, new Date());
   }
   
   public OperationId(final Long anIdOp, final Date aSendDate)
   {
      this.idOp = anIdOp;
      this.sendDate = aSendDate;
   }
   
   /**
    * @return {@link Long} - Generated id
    */
   @Column(name = "ID")
   public Long getIdOp()
   {
      return idOp;
   }
   
   /**
    * @param idOp - Generated id
    */
   public void setIdOp(Long idOp)
   {
      this.idOp = idOp;
   }
   
   /**
    * @return {@link Date} - Other field
    */
   @Column(name = "SEND_DATE", nullable = false)
   public Date getSendDate()
   {
      return sendDate;
   }
   
   /**
    * @param sendDate - Other field
    */
   public void setSendDate(Date sendDate)
   {
      this.sendDate = sendDate;
   }
}


With some work, it could be fully operationnal even for non getter / setter field access (idOp would be public in this case).


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.