-->
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.  [ 13 posts ] 
Author Message
 Post subject: Using generator with composite-key
PostPosted: Tue Jul 19, 2005 11:16 pm 
Newbie

Joined: Fri May 27, 2005 3:19 pm
Posts: 18
Hibernate version: 3.0

Name and version of the database you are using: MS SQL 7.0

Is it possible to use a generator for one of the key-property of the composite-id or a generator can only be used with a single primary key?
For example:


<class name="BookPage" table="BookPage">
<composite-id>
<key-property name="number"/>
<key-property name="status" />
</composite-id>
.......
I want the number of the page handled by a generator and status will be set within the application.

Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 20, 2005 12:12 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I believe it can be done by writing a CompositeUserType together with an IdentifierGenerator. I have never done it though.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 20, 2005 2:08 am 
Expert
Expert

Joined: Thu Jan 29, 2004 2:31 am
Posts: 362
Location: Switzerland, Bern
Positiv! We've successfully implemented this like this:

Mapping:
Code:
      <id name="myPk" type="ch.bedag.gba.cap.server.persistence.hibernate.CapiPkType" access="field">
         <column name="FBM01" sql-type="systemId" not-null="true"/>
         <column name="FBM02" sql-type="id" not-null="true"/>
         <generator class="ch.bedag.gba.cap.server.persistence.hibernate.CapiIdentifierGenerator"/>
      </id>


The user type:
Code:
public class CapiPkType implements CompositeUserType {
   
   private static final ch.bedag.gba.common.logging.CapiLogger LOG = ch.bedag.gba.common.logging.CapiLoggerFactory
               .getLog(CapiPkType.class);
   
   private static final String[] PROPERTY_NAMES = {"systemId", "id"};
   private static final Type[] PROPERTY_TYPES = {Hibernate.LONG, Hibernate.LONG};

   public String[] getPropertyNames() {
      return PROPERTY_NAMES;
   }

   public Type[] getPropertyTypes() {
      return PROPERTY_TYPES;
   }

   public Object getPropertyValue(Object component, int property) throws HibernateException {
      CapiPK capiPk = (CapiPK)component;
      switch (property) {
      case 0:
         return capiPk.getSystemId().getId();
      case 1:
         return capiPk.getId();
      default:
         throw new PersistenceRuntimeException(property+" ist kein gütliger Wert für property (muss 0 oder 1 sein.");
      }
   }

   public void setPropertyValue(Object component, int property, Object value) throws HibernateException {
      CapiPK capiPk = (CapiPK)component;
      switch (property) {
      case 0:
         capiPk.setSystemId(new SystemId((Long)value));
      case 1:
         capiPk.setId((Long)value);
      default:
         throw new PersistenceRuntimeException(property+" ist kein gütliger Wert für property (muss 0 oder 1 sein.");
      }
   }

   public Class returnedClass() {
      return CapiPK.class;
   }

   public boolean equals(Object x, Object y) throws HibernateException {
      if (x == y) return true;
      if (x == null || y == null) return false;
      return x.equals(y);
   }

   public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
      if (LOG.isTraceEnabled()) {
         LOG.trace("Names: "+new ReflectionToStringBuilder(names, ToStringStyle.MULTI_LINE_STYLE).toString());
         if (owner!=null) {
            LOG.trace("Owner: "+owner.getClass().getName());
         }
         else {
            LOG.trace("Owner: null");
         }
      }
      if (rs.getObject(names[0])!=null && rs.getObject(names[1])!=null) {
         long systemId = rs.getLong(names[0]);
         long id = rs.getLong(names[1]);
         return new CapiPK(systemId, id);
      }
      else {
         return null;
      }
   }

   public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
      if (value==null) {
         st.setNull(index, Types.NUMERIC);
         st.setNull(index+1, Types.NUMERIC);
      }
      else {
         CapiPK capiPk = (CapiPK)value;
         if (capiPk.getSystemId()==null) {
            st.setNull(index, Types.NUMERIC);
         }
         else {
            st.setLong(index, capiPk.getSystemId().getId().longValue());
         }
         if (capiPk.getId()==null) {
            st.setNull(index+1, Types.NUMERIC);
         }
         else {
           st.setLong(index+1, capiPk.getId().longValue());
         }
      }
   }

   public Object deepCopy(Object value) throws HibernateException {
      return value;
   }

   public boolean isMutable() {
      return false;
   }

   public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException {
      return (Serializable)value;
   }

   public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException {
      return cached;
   }

}


The generator:
Code:
public class CapiIdentifierGenerator implements IdentifierGenerator {
   
   public CapiIdentifierGenerator() {
   }
   
   public Serializable generate(SessionImplementor session, Object object) {
       // your implementation
   }
}



HTH
Ernst


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 20, 2005 3:31 am 
Newbie

Joined: Fri May 27, 2005 3:19 pm
Posts: 18
Thank you for your response and sample code. Considering that I just need a hibernate packaged generator for one property and second property will be a string value set within the application, that seem to be an overkill for my case. I was wondering if there is a more hiernate native way of doing it, but I guess not :(


Top
 Profile  
 
 Post subject: what about a foreign key in the composite-id
PostPosted: Wed Jan 23, 2008 3:22 am 
Newbie

Joined: Thu Jun 01, 2006 8:17 am
Posts: 10
Location: mostly lake of constance
thanks for your posting, even though your posting is 2 and a half year old.

I have nearly the same problem like you, that i have a composite-key and one of the key-properties (a index) should be generated. In my case, the non generated property is foreign-key to another entity. Any Ideas how to handle this problem with hibernate. I guess it's nearly the same procedure like ernst_pluess posted, isn't it?

Cheers

René


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2008 4:31 am 
Newbie

Joined: Mon May 19, 2008 12:42 am
Posts: 10
René,
Can you please let me know if you were able to implement this in any different way?


Top
 Profile  
 
 Post subject: no fancy solution
PostPosted: Tue Sep 30, 2008 6:31 am 
Newbie

Joined: Thu Jun 01, 2006 8:17 am
Posts: 10
Location: mostly lake of constance
hi,
hibernate misses support for this problem. i solved it in the following way:

1. for the entities such a composite key is needed i wrote a primkey class
2. I wrote an interceptor which checks whether the property value which should be generated is 0. if the value is 0 I create an unique sequence value using a PrepareStatement. Because we use Oracle this works. with hslqldb for example this won't work i think.

this solution is not sexy and still has some pitfalls, but it achieves our needs.

regards,
René

_________________
regards,
rene
__________________________
please rate if posting helps you!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 01, 2008 8:14 am 
Newbie

Joined: Mon May 19, 2008 12:42 am
Posts: 10
Thanks Rene ; i tried to implement the Interceptor approach but facing a problem. Can you please help?
The persistent class Request contains the PK class PKey :
Code:
public class Request  {
..

private PKey pk;
..
}

The Hibernate mapping is :
Code:
<class name="com.xyz.entity.Request"
        table="xyz_REQUEST" schema="xyz">
<composite-id name="pk" class="com.xyz.dataaccess.PKey">
       
              <key-property name="siteId"
                  type="java.lang.String">
                  <column name="XYZ_REQUEST_SITE_ID" length="1" not-null="true" />
              </key-property>
              <key-property name="id" type="java.lang.Long">
                  <column name="XYZ_REQUEST_ID"  />
              </key-property>
</composite-id>


The interceptor onSave() looks like :

Code:
public boolean onSave(Object entity,
            Serializable id,
            Object[] state,
            String[] propertyNames,
            Type[] types)
   {
      
      ((PKey) id).setId(new Long(801550));
      ((PKey) id).setSiteId("E");
      ((Request) entity).setPk((PKey) id);


Here, i'm trying to manually set the composite key parts (will update this to fetch the Id from DB etc. later) , but on execution the following exception is thrown:

Code:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): com.bofa.ecom.common.core.entity.Request
   at org.hibernate.id.Assigned.generate(Assigned.java:56)
   at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
   at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
   at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
   at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:562)
   at org.hibernate.impl.SessionImpl.save(SessionImpl.java:550)
   at org.hibernate.impl.SessionImpl.save(SessionImpl.java:546)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 02, 2008 12:57 am 
Newbie

Joined: Thu Jun 01, 2006 8:17 am
Posts: 10
Location: mostly lake of constance
Hi jaalax,
I think the problem with your approach is, that you don't set the PK before make a session.save(). try to set the PK of your entity before making a save and set just the values in your PK class using the interceptor.

In our solution we only the the generated id using the interceptor. the manually values or references are always set before save() etc.

_________________
regards,
rene
__________________________
please rate if posting helps you!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 03, 2008 8:43 am 
Newbie

Joined: Mon May 19, 2008 12:42 am
Posts: 10
Thanks Rene ! , your suggestion helped - thinking back, does it provide any benefit if we are anyways going to use JDBC to generate the identifier in the interceptor instead of doing it in the DAO itself - just that if we need to generate the same identifier from multiple DAOs, it's centralized..just wanted to confirm what the driving factor was for creating the key in the interceptor

PS: I am yet to figure how to rate a post, but will definitely do it when i find out..thx again!


Top
 Profile  
 
 Post subject: rating
PostPosted: Thu Oct 09, 2008 6:25 pm 
Newbie

Joined: Thu Jun 01, 2006 8:17 am
Posts: 10
Location: mostly lake of constance
i guess rating works only for your own thread. nevertheless, good to see my post helped you.

_________________
regards,
rene
__________________________
please rate if posting helps you!


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 12, 2008 11:34 pm 
Newbie

Joined: Sun Oct 12, 2008 2:06 am
Posts: 3
Hi jaalex

How did you manage to assign the value before onSave(). Please explain.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Oct 13, 2008 12:05 am 
Newbie

Joined: Mon May 19, 2008 12:42 am
Posts: 10
techno123,

Say if the class to be persisted is Person, which has a PK class of PKey, then you just do :
Code:
person.setPKey(new PKey());


before calling session.save() . You can also set some attributes of the composite key to the entity, while the remaining key attributes can be set in the interceptor.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 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.