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 :
* @GenericGenerator(
* name = "OPERATION_ID_SEQ",
* strategy = "fr.sfr.sivpn.persistance.sequence.EmbeddedSequenceGenerator",
* parameters =
* {
* @Parameter(name = "sequence", value = "ID_RTE_SEQ"),
* @Parameter(name = "className", value = "fr.sfr.sivpn.persistance.entity.engine.OperationEntity"),
* @Parameter(name = "idGetterMethod", value = "getId"),
* @Parameter(name = "idClassName", value = "fr.sfr.sivpn.persistance.entity.ids.OperationId"),
* @Parameter(name = "generatedFieldGetterMethod", value = "getIdOp"),
* @Parameter(name = "idClassHibernateType", value = "org.hibernate.type.LongType"),
* @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).