-
1. Re: EmbeddedId and GeneratedValue
bill.burke Mar 3, 2006 4:29 PM (in response to nholbrook)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
nholbrook Mar 6, 2006 5:30 PM (in response to 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 Mar 31, 2006 7:24 AM (in response to nholbrook)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 Mar 31, 2006 7:36 AM (in response to nholbrook)your solution can be very inefficient and conceptually does not work. you need to think about concurrent transactions.
-
6. Re: EmbeddedId and GeneratedValue
squishy Apr 1, 2006 6:04 AM (in response to nholbrook)i thoguht so. do you have an idea for working solution? i'd be very thankful :)
-
7. Re: EmbeddedId and GeneratedValue
epbernard Apr 3, 2006 5:53 AM (in response to nholbrook)the one provided by the spec :-)
TABLE is very efficient, clean and respectful of your Tx concurrency. -
8. Re: EmbeddedId and GeneratedValue
squishy Apr 3, 2006 5:54 AM (in response to nholbrook)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 Apr 3, 2006 7:12 AM (in response to nholbrook)(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 Apr 3, 2006 12:42 PM (in response to nholbrook)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 Apr 3, 2006 4:44 PM (in response to nholbrook)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 Apr 5, 2006 3:09 AM (in response to nholbrook)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 Apr 5, 2006 3:16 AM (in response to nholbrook)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 Apr 5, 2006 3:16 PM (in response to nholbrook)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 :(