-->
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.  [ 16 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: natural-id and generated id - best practice ?
PostPosted: Wed May 24, 2006 9:56 am 
Newbie

Joined: Sat Sep 24, 2005 10:44 am
Posts: 12
Location: Berlin, Germany
hi all,

i have the following simple problem:

i'm working with hibernate 3.1 inside the springframework. I use postgresql as DB.
i have one Table A with the following mapping:

Code:
<class name="A" table="a" >
<id name="id" column="ot_id" >
   <generator class="native">
   <param name="sequence">a_seq_id</param>
   </generator>
</id>
<natural-id >
<many-to-one name="b" class="B" column="b" access="property"  />
<many-to-one name="c" class="C" column="c" access="property" />
<many-to-one name="d" class="D" column="d" access="property" />
</natural-id>

<property name="number" column="nr" />
</class>


The table is aquivalent to that mapping.
the Classes have correct hashCode and equals methods.

What i want to do is:

    store the Entity A with valid values.
    load the Entitiy A
    change the number-value and setting the id to -1 (do not ask why, if i have the id, i have no problem)
    merge the Entitiy A (with allready existing natural-id )

at the last step i got an exception. the "unique-key is allready exist" . Hibernate should update the row, but it tries to insert a new row and i dont know why.

How must i configure the mapping so that the class A with id = -1 should be updated on change, if the unique key allready exist ???


Martin Sachs
act-intl

Hibernate version: 3.1.3

Name and version of the database you are using:Postgresql 8.1


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 24, 2006 11:09 am 
Regular
Regular

Joined: Mon May 22, 2006 2:30 pm
Posts: 74
There doesn't seem to be enough detail in your post to determine what is actually happening. It would help to place some actual code snippets that show exactly what you are doing.


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 27, 2006 4:01 am 
Beginner
Beginner

Joined: Sat Dec 17, 2005 1:24 pm
Posts: 42
Location: Berlin, Germany
Hi!

I don't know what happens if you use "generated key" and "natural key" together. I thought, they would be mutually exclusive.

However, I believe Hibernate has a very simple algorithm to decide whether to update or to insert. If the generated key is not initialized (i.e. if it still has the value specified in the unsaved-value property [see http://www.hibernate.org/hib_docs/v3/reference/en/html_single/#mapping-declaration-id]) it inserts a new row, otherwise it updates the row indicated by the primary key.

Did this help in any way?

All the best,

René


Top
 Profile  
 
 Post subject: insert or update by natural id or unique key ?
PostPosted: Sat May 27, 2006 7:17 am 
Newbie

Joined: Sat Sep 24, 2005 10:44 am
Posts: 12
Location: Berlin, Germany
hi

let me try to explain my little problem a more concret.

I have made a sample to demonstrate it. So here are the mapping and the testclasses:

Code:
<class name="de.informaticum.orm.hibernate.test.EntityA" table="entityA" >
<id name="id" column="id" type="java.lang.Long" unsaved-value="-1">
      <generator class="native">
         <param name="sequence">entitya_seq</param>
      </generator>
   </id>
   <natural-id >
      <property name="uniquekey1" />
      <property name="uniquekey2" />
   </natural-id>
   
   <property name="nonunique" />
</class>


I have a little helperclass to use the springframework with the HibernateTemplate.


Code:
public class EntityManager {
    private static final Logger log = Logger.getLogger(EntityManager.class);
   
    HibernateTemplate ht= null;
   
    public EntityManager(HibernateTemplate tm) {
        this.ht = tm;
    }
    // load the last Row of the DB-Table
    public EntityA loadLastRow() {
        List<EntityA> list=null;
        try {
            list = ht.find("from EntityA a");
        } catch (DataAccessException e) {
            log.error(e.getMessage());
        }
        if (list!=null && list.size()>0) {
            return list.get(list.size()-1);
        }
        return null;
    }
    // merge the EntityA to the DB and get an actual instance.
    public EntityA merge(EntityA entity) {
        try {
            return (EntityA) ht.merge(entity);
        } catch (DataAccessException e) {
            log.error(e.getMessage());
            return null;
        }
    } 
}



The testmain :

Code:
public class TestMain {
    /**
     * @author Martin Sachs
     * @since 24.05.2006
     * @param args
     */
    public static void main(String[] args) {
       
        ...

        HibernateTemplate ht = null;
        ...
        // getting the hibernateTemplate here
        ...
           
            EntityManager manager = new EntityManager(ht );
           
            // create new Object
            EntityA entity = new EntityA(1L,new Date().getTime(),"value");
            // store Object to DB
            manager.merge(entity);
           
            // get last row
            EntityA loadedEntity = manager.loadLastRow();
            if (loadedEntity!=null) {
                System.out.println(loadedEntity.toString());
            }else {
                System.out.println("no row fetched");
            }
           
            if (loadedEntity!=null) {
                // modify Object
                loadedEntity.setNonunique("changed value");
                // setting id to -1, because it could be transient or generated
                // by someone manually

--->         loadedEntity.setId(-1L); <<---- here is my problem

I want to the the ID to -1  but i want to update the DB-Row with a merge.


                // store modified entity
                System.out.println("Store modified entity");
                EntityA storedEntity = manager.merge(loadedEntity);
                if (storedEntity!=null) {
                    System.out.print("modification stored: ");
                    System.out.println(loadedEntity.toString());
                }else {
                    System.out.println("modification not stored");
                }
            }
           
            ...
        }
    }
}


My Question is:

Is there a way to map a class with an id as primary key and unique key as natural-id to decide whether the unique key is allready in DB or not (hibernate make update or instert by natural-id and not by primary id) ?

If you ask why not by primary key: The primary key should never be tranported over the Database layer. So i want to make the ID transient and transport the mapping object to the client. (Dettached object)

I know that i can select each object and get the IDs bevor i merge. Can hibernate help in that case ???

Martin Sachs



the class EntityA:

Code:
public class EntityA implements java.io.Serializable{
    private transient Long id = -1L;
    private Long uniquekey1;
    private Long uniquekey2;
    private String nonunique;
    ...
    // getter and setter
    ...
}


[/code]


Top
 Profile  
 
 Post subject: usage of transient ID declaration
PostPosted: Tue May 30, 2006 3:41 am 
Newbie

Joined: Sat Sep 24, 2005 10:44 am
Posts: 12
Location: Berlin, Germany
hi all,

if i use a cache (EHCache, TreeCache) i can fetch all Objects with by unique key (that are used in equals()) and get its primarykey from the cache. But this is not a solution for this problem at all. I have to cache all objects, that was created all the time.

If someone see a better solution, reply it please ! Maybe there is a hibernate mapping solution ?

If not, why should i use the natural-id Tag ?

All the best,

Martin Sachs


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 30, 2006 6:10 am 
Newbie

Joined: Fri Feb 10, 2006 11:19 am
Posts: 5
Same problem described here


http://forum.hibernate.org/viewtopic.php?t=958514


Top
 Profile  
 
 Post subject: how to get id by surrogatekeys ?
PostPosted: Tue May 30, 2006 7:05 am 
Newbie

Joined: Sat Sep 24, 2005 10:44 am
Posts: 12
Location: Berlin, Germany
hi

This referenced artikel dont show a solution. There a lot of Workarounds for this problem, but all have its problems.

I have tried to use a Cache, that could store the Object with the ID. The EHCache caches the objects by the id hibernate and not by the object.hashCode() - methode.

How can i configure the EHCache to use hashCode() - Methode ???

I have set the id to -1 und the unsaved-value="undefined".

if the state "unsave" or "notunsave" cant evaluate from the id, hibernate ckecks the interceptor, than an persister and then the EHCache.


I know that one solution is the using of composite-id

Why cant we use natural-id as unsaved-value="bynatural-id" ???

martin


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 30, 2006 9:53 am 
Newbie

Joined: Fri Feb 10, 2006 11:19 am
Posts: 5
Hi ,


I agree. I just added the reference so users are aware that useful information on this problem may be posted elsewhere.

Tim


Top
 Profile  
 
 Post subject: any resolution to this one?
PostPosted: Fri Jun 09, 2006 12:10 pm 
Regular
Regular

Joined: Fri Jan 28, 2005 3:11 am
Posts: 81
any resolution to this one yet?

I have the same problem


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 09, 2006 2:25 pm 
Expert
Expert

Joined: Fri Aug 19, 2005 2:11 pm
Posts: 628
Location: Cincinnati
Changing the primary key is a bad thing to do. Hibernate attempts to keep its in memory version of the data consistent with what's in the database. If you change the id of objects, hibernate thinks it is a new object because it is! This is why it is trying to save it.

the descriptoin for the merge function says " If the given instance is unsaved, save a copy of and return it as a newly persistent instance"

I think a better way to handle this is to either manually move the attributes of the object to a new one or updating the attributes of the object with the correct pk that has been loaded from the database.

_________________
Chris

If you were at work doing this voluntarily, imagine what you'd want to see to answer a question.


Top
 Profile  
 
 Post subject: clarification
PostPosted: Fri Jun 09, 2006 3:04 pm 
Regular
Regular

Joined: Fri Jan 28, 2005 3:11 am
Posts: 81
thanks, but could you clarify what you mean in light of the elaboration below

I have a sequence-based primary key, but also another field that is a natural, candidate key. When I saveOrUpdate, I want it to be smart enough to realize - since the natural key matches, don't create a new record, but instead update the existing one. At present, it only seems to look at the primary key, and since that object doesn't have any yet, it generates a new one from the sequence and tries inserting a new record resulting in unique constraint error on that natural, candidate key field.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 09, 2006 3:27 pm 
Expert
Expert

Joined: Fri Aug 19, 2005 2:11 pm
Posts: 628
Location: Cincinnati
That's never going to work because hibernate only uses primary keys when determining if an object exists already.

If you know that much data (candidate key) you should load that object into memory from the database, update the attributes in memory, then update it in the database.

B/C you don't know the pk, use a hql query to load the object based on the candidate key data.

_________________
Chris

If you were at work doing this voluntarily, imagine what you'd want to see to answer a question.


Top
 Profile  
 
 Post subject: thanks
PostPosted: Fri Jun 09, 2006 3:54 pm 
Regular
Regular

Joined: Fri Jan 28, 2005 3:11 am
Posts: 81
aha, ok - I realized I can prevent attempt to insert a duplicate by loading it from db first, but I was hoping that I can get it to somehow look at the natural key and behave just as it would had the primary key been the same

so I guess you confirmed that I can't - thank so much

too bad the system won't let me give you points, since I wasn't the original question author


Top
 Profile  
 
 Post subject: Load ID by Natural ID or Unique key - properties.
PostPosted: Tue May 29, 2007 8:44 am 
Newbie

Joined: Sat Sep 24, 2005 10:44 am
Posts: 12
Location: Berlin, Germany
Hi again,

i dont have a good solution for my problem yet. I can load all data from db first. Compary each object with equal ( equality of natural-id) and getting the primary key for one object.


Code:
List oldDB = hibernateTemplate.find("from A a");

for(Object o1: newDB){
  for(Object o: oldDB){
     if (o1.equals(o)){
       o1.setID(o.getID());
     }
  }
}


I have to mention, that i use two instances of Postgres und must send all data to the second database. There i have to check all objects first. I dont change the primary key - i dont have THE Primary key at the second DB. So i need to find alle objects of all Tables to get an ID by unique key or naturalid


Have someone an idea for this ? Maybe an hibernate listener concept ? or a custom entitypersister?

regards

Martin Sachs


Top
 Profile  
 
 Post subject: a solution
PostPosted: Thu May 31, 2007 3:56 am 
Newbie

Joined: Thu May 31, 2007 3:27 am
Posts: 11
If what you all want to do is to load an object from a database using its natural-id instead of its surrogate key, you can use information provided by the mapping file.

This code returns a persisted object, if any, that match the natural id given by the passed in "entity". Of course, the hbm file associated to the entity has to specify which properties make the natural-id.

Code:
  public Object getWithNaturalId(Object newEntity,SessionFactory factory) throws Exception{
   
    Object persisted=null;

    Class entityClass = newEntity.getClass();
    ClassMetadata meta = factory.getClassMetadata(entityClass);

    int[] nIdxs = meta.getNaturalIdentifierProperties();

    if (nIdxs != null && nIdxs.length > 0) {

      Session session = factory.openSession();
      Criteria crit = session.createCriteria(entityClass);

      for (int j = 0; j < nIdxs.length; j++) {

        String name = meta.getPropertyNames()[n[j]];
        Object value = PropertyUtils.getProperty(newEntity, name);

        crit.add(Restrictions.eq(name, value));
      }

       persisted = crit.uniqueResult();
     
    }
    return persisted;
   
  }


You'll need the commons-beanutils package.


Last edited by letrait on Thu May 31, 2007 4:03 pm, edited 1 time in total.

Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 16 posts ]  Go to page 1, 2  Next

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.