-->
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.  [ 8 posts ] 
Author Message
 Post subject: saveOrUpdate using NaturalID with Surrogate Primary Key
PostPosted: Mon Feb 23, 2009 11:54 am 
Newbie

Joined: Mon Feb 23, 2009 11:16 am
Posts: 17
Hibernate version: 3.3.1GA

Mapping documents:
Code:
@Entity
public class DefaultInstrument implements Instrument {

   @Column(length = 6, nullable = false)
   String groupCode;

   @NaturalId(mutable = false)
   @Column(length = 6, updatable = false, nullable = false)
   String productCode;

   // This is nullable because this value is NOT required for order entry.
   @Column(length = 20)
   String securityDescription;

   @Id
   @GeneratedValue
   long id;

   @Column(length = 12, nullable = false)
   long securityId;

/** getters/setters **/
}


Code between sessionFactory.openSession() and session.close():
This is a bit complicated and spread through a few classes.

getHibernateTemplate().saveOrUpdate(updateData); is the final call made, I'm using Spring 2.5 so I go through a Hibernate Template, this manages sessions and JDBC connections for me, the applications ORM is simple and only contains this one Insturment object for now.

What I'm doing is creating an Instrument object with productCode (naturalId) and all other fields except ID populated then passing it to my DAO which calls saveOrUpdate. This object is properly assigned an id of 1. I then receive another Instrument object with the same naturalId but possibly different values for non-key fields. This is a transient object which I also send to my DAO through saveOrUpdate. Here is my issue, saveOrUpdate looks at the id (null/0) and decides this object needs to be save'd not updated. So I looked around and was told the proper way to implement this is via an Interceptor and overriding the isTransient() method.

So I did this and inside the function I call the following:

Code:
if(entity instanceof Instrument) {
         Instrument tempInstrument = (Instrument)entity;
         Instrument fromDB = instrumentDataStore.load(tempInstrument);
         if(fromDB != null) {
            tempInstrument.setId(fromDB.getId());
            return false;
         }
      }
      return super.isTransient(entity);


Now at first I tried this without setting the ID but it didn't work, Hibernate still went and did a snapshot search based on the null/0 ID instead of the Natural ID if all I did was return false after testing that the natural ID exists.

Am I approaching this situation the wrong way or does this seem correct? I'm currently writing a unit test for a simplified example of my issue but more importantly I'm concerned that I broke hibernate by setting this id myself. For example I'm not sure if what I'm doing causes two instances to represent the same row (same id) within one Unit of Work, if this is what I did how much of an issue is it?

I'm thinking I can keep a set of these object myself (thus only the newest version survives) and then batchSave or something all of them at the end, this makes my architecture bottleneck (potentially) if not I have to design to prevent bottle necks and I rather like being able to just receive an Instrument and send it off to my DAO service without any concern.

Full stack trace of any exception that occurs: N/A

Name and version of the database you are using:
Derby 10.4

The generated SQL (show_sql=true):
This is in my example above.

Debug level Hibernate log excerpt:
This is also in my example above.

Ideas, questions?

Thanks.

I'll post a Unit Test to clarify the situation when I'm done with it.


Last edited by soks86 on Mon Feb 23, 2009 4:42 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 23, 2009 2:33 pm 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
Why do you want to use an interceptor at all? Coz in your interceptor also you are doing a load() to get the id value. Then you can do this in your business logic also, rite?

_________________
Regards,
Litty Preeth


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 23, 2009 3:09 pm 
Newbie

Joined: Mon Feb 23, 2009 11:16 am
Posts: 17
My business logic is not aware of any persistance. I call service.doWork(data) and if the service is a DAO it will call saveOrUpdate internally and store my data.

I can put this load and Natural ID checking inside of the DaoService.saveOrUpdate() call and then merge the data in... that makes a good amount of sense even.

Right now my AbstractDao has this:
Code:
@Override
public void saveOrUpdate(T updateData) {
   getHibernateTemplate().saveOrUpdate(updateData);
}


So for a manual load/merge I suppose I can do (within my InstrumentDao which derives from AbstractDao):
Code:
@Override
public void saveOrUpdate(T updateData) {
   Instrument fromDb = load(updateData);
   if (fromDb == null) {
      getHibernateTemplate().save(updateData);
   } else {
      updateData.setId(fromDb.getId());
      updateData = (Instrument) getHibernateTemplate().merge(updateData);
   }
}


I can even live with this solution however I am still a little shocked that saveOrUpdate() has no mechanism for Natural ID comparisons and I was originall looking for a "best implementation" solution. I had gotten the Interceptor idea from some interviews I read with Hibernate developers.

If I do go with this new saveOrUpdate() is there a better way to insert into a DB where x=y and just insert incase you can't find x=y? (like in one statement and one DB call instead of 2).

Anyone else care to opine on which implementation is better? Interceptor or wrapper-saveOrUpdate()?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 24, 2009 1:44 am 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
There is no way you can implement this without a load. And IMHO incorporating this logic to ur AbstractDao would be better than having an interceptor coz it will reduce the complexity and overhead.

_________________
Regards,
Litty Preeth


Top
 Profile  
 
 Post subject: Re: saveOrUpdate using NaturalID with Surrogate Primary Key
PostPosted: Tue Feb 24, 2009 4:32 am 
Beginner
Beginner

Joined: Wed Nov 19, 2008 8:25 am
Posts: 46
Location: Saint Petersburg, Russian Federation
soks86 wrote:
...

What I'm doing is creating an Instrument object with productCode (naturalId) and all other fields except ID populated then passing it to my DAO which calls saveOrUpdate. This object is properly assigned an id of 1. I then receive another Instrument object with the same naturalId but possibly different values for non-key fields. This is a transient object which I also send to my DAO through saveOrUpdate. Here is my issue, saveOrUpdate looks at the id (null/0) and decides this object needs to be save'd not updated.
...


I don't see that you benefit from using surrogate id at your case. If you use a single natural id you achieve necessary behavior without any unnecessary complexity. I.e. if you define a contract that DefaultInstrument.productCode should always be defined at the DefaultInstrument object given to the DAO, you just map DefaultInstrument.productCode with @Id annotation and that's all.


Top
 Profile  
 
 Post subject: Re: saveOrUpdate using NaturalID with Surrogate Primary Key
PostPosted: Tue Feb 24, 2009 5:02 pm 
Newbie

Joined: Mon Feb 23, 2009 11:16 am
Posts: 17
denis.zhdanov wrote:

I don't see that you benefit from using surrogate id at your case. If you use a single natural id you achieve necessary behavior without any unnecessary complexity. I.e. if you define a contract that DefaultInstrument.productCode should always be defined at the DefaultInstrument object given to the DAO, you just map DefaultInstrument.productCode with @Id annotation and that's all.


I wish this was the case but every text I've read suggests that a surrogate key is always the best method for future expandability. If my business key ever needs to change I want to be able to change it (I did change it to be updatable recently so I suppose in my previous example you are correct). Also the chance of corrupting foreign keys or anything similar also seems possible.

I don't know this as well as I likely should but it's been hammered into my head so many times that everything I create has a surrogate key. I suppose the performance gains might be some if I removed it but still... I would need a few professional opinions to go in that direction.

Thank you though.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 25, 2009 12:35 am 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
If you are 100% sure tht you wont require to make ur natural key mutable then you can give away with the surrogate key and use ur natural key as the primary key. But if that is not the case then it is better to hav a surrogate key.

_________________
Regards,
Litty Preeth


Top
 Profile  
 
 Post subject: Re: saveOrUpdate using NaturalID with Surrogate Primary Key
PostPosted: Wed Feb 25, 2009 4:56 am 
Expert
Expert

Joined: Thu Jan 08, 2009 6:16 am
Posts: 661
Location: Germany
soks86 wrote:
So I did this and inside the function I call the following:

Code:
if(entity instanceof Instrument) {
         Instrument tempInstrument = (Instrument)entity;
         Instrument fromDB = instrumentDataStore.load(tempInstrument);
         if(fromDB != null) {
            tempInstrument.setId(fromDB.getId());
            return false;
         }
      }
      return super.isTransient(entity);


Now at first I tried this without setting the ID but it didn't work, Hibernate still went and did a snapshot search based on the null/0 ID instead of the Natural ID if all I did was return false after testing that the natural ID exists.

Why do you use load if you want to search by naturalId? You could create a query ("from Instrument where productCode = :productCode") and invoke session.getNamedQuery(...).setParameter("productCode", tempInstrument.getProductCode()).uniqueResult() to check if an entity is transient.

_________________
-----------------
Need advanced help? http://www.viada.eu


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