Solved

hibernate saveOrUpdate

Posted on 2006-06-09
8
17,189 Views
Last Modified: 2013-11-24
I have a PROGRAM table.  It's keyed by program_id generated by an oracle sequence.  It also has a program_name field which is not null and unique.

Here's how it's mapped:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
      <class name="smefsModel.pojo.Program" table="PROGRAM" schema="SMEFS">
            <id name="programId" column="PROGRAM_ID" type="long">
                  <generator class="sequence">
                        <param name="sequence">SMEFS_SEQ</param>
                  </generator>
            </id>
            <natural-id mutable="true">
                  <property name="programName" type="string" column="PROGRAM_NAME" length="30" unique="true" not-null="true" />
            </natural-id>
            <property name="programDesc" type="string" column="PROGRAM_DESC" length="100" />
      </class>
</hibernate-mapping>

So here the question: is there a built-in way to make sure that when I try to saveOrUpdate a program and it has the same program_name, it would automatically know to just update the existing one and not to generate a new sequence-based program_id.

So, for example, currently, when I do:
1      Program program1 = new Program("yo");
2      session.saveOrUpdate(program1);
3      Program program2 = new Program("yo");
4      program2.setProgramDesc("whatup");
5      session.saveOrUpdate(program2);

For line 5, I get:    
insert
    into
        SMEFS.PROGRAM
        (PROGRAM_NAME, PROGRAM_DESC, PROGRAM_ID)
    values
        (?, ?, ?)
10:45:50,866 DEBUG StringType:79 - binding 'yo' to parameter: 1
10:45:50,866 DEBUG StringType:71 - binding null to parameter: 2
10:45:50,866 DEBUG LongType:79 - binding '301028540' to parameter: 3
10:45:50,882  WARN JDBCExceptionReporter:71 - SQL Error: 1, SQLState: 23000
10:45:50,882 ERROR JDBCExceptionReporter:72 - ORA-00001: unique constraint (SMEFS.PROGRAM_UX1) violated

because it tries to insert a new record (and then, of course, it bombs since program_name is unique), instead of updating the existing one.  This happens because the key to the table is the sequence-based program_id rather than program_name.


0
Comment
Question by:aturetsky
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 4
8 Comments
 
LVL 6

Expert Comment

by:phuocnh
ID: 16879659
Hi aturetsky !
Please read here:
http://www.systemmobile.com/articles/IntroductionToHibernate.html#Creating%20and%20Updating%20Persistent%20Classes
Focus on the following lines:
"The saveOrUpdate(Object) call will save the object if the id property is null, issuing a SQL INSERT to the database. This refers to the unsaved-value attribute that we defined in the Player mapping document. If the id is not null, the saveOrUpdate(Object) call would issue an update, and a SQL UPDATE would be issued to the database. (Please refer to the sidebar Unsaved-Value Strategies for more information on this topic.)"
And here about "Unsaved Value Strategies"
The unsaved-value attribute supported by the id element indicates when an object is newly created and transient, versus an object already persisted. The default value is null, which should be sufficient for most cases. However, if your identifier property doesn't default to null, you should give the default value for a transient (newly created) object.Additional values supported by the unsaved-value attribute are: any, none and id-value.
Phuoc
0
 
LVL 1

Author Comment

by:aturetsky
ID: 16881963
I don't think unsaved-value is applicable here, since at the point of saveOrUpdate my instance is certainly newly created and transient.  It's just that when that newly created instance has a program_id key that matches one already in the database, hibernate is smart enough to issue an update rather than insert.  For example if I have a record with program_id 123 already in the DB and then do:

Program program = new Program();
program.setProgramId(new Long(123));
program.setProgramName("blah");
session.saveOrUpdate(program);

hibernate is smart enough to realize that the record with that program_id is already in DB and to issue an update and not to insert a new record with a new sequence-based program_id

but if I have a record with program_name "blah" already in the DB and then do:

Program program = new Program();
program.setProgramName("blah");
program.setProgramDesc("ssup");
session.saveOrUpdate(program);

hibernate doesn't realize that a record with program_name "blah" is already sitting in the database and that all that needs to be done is an update on a program_desc, but rather attempts to issue an insert with a new sequence-based program_id, resulting in a unique constraint violation error on the program_name field

So I am not sure how unsaved-value would help me here.

0
 
LVL 6

Expert Comment

by:phuocnh
ID: 16882378
Hi aturetsky!
Quote:
but if I have a record with program_name "blah" already in the DB and then do:

Program program = new Program();
program.setProgramName("blah");
program.setProgramDesc("ssup");
session.saveOrUpdate(program);

hibernate doesn't realize that a record with program_name "blah" is already sitting in the database and that all that needs to be done is an update on a program_desc, but rather attempts to issue an insert with a new sequence-based program_id, resulting in a unique constraint violation error on the program_name field
My idea:
The objects is distinguished between each others by object identifier.
In your example, the record in your db with program_name "blah" is associated object with object identifier x because programe_name is not an object identifier.
When you do the following task:
Program program = new Program();
program.setProgramName("blah");
program.setProgramDesc("ssup");
session.saveOrUpdate(program);
your program object has an identifier whose value is y.
x<>y so they are two different objects so hibernate must be use "insert" instead of update.
Your table with program_name is unique why don't you use it as identifier of the objects?
Phuoc

 
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Author Comment

by:aturetsky
ID: 16882639
Good DB design practices and hibernate authors recommend refraining from using a natural id as a key.  For one thing, despite its uniqueness, its value may be changed later on.  That's why I did not want to use program_name as key, and created a surrogate, sequential program_id key instead.

I realize it looks at the key to determine whether to insert or update.  I wish however, that using the natural-id element in hibernate mapping would not only tag it as unique and not null, but also have impact on the insert update behavior.
0
 
LVL 6

Expert Comment

by:phuocnh
ID: 16882792
Hi aturetsky!
I think you should use merg for the first time and saveOrUpdate for another time
For first time:
program1 = merge(program1);
The other times:
Program program = new Program();
program.setProgramId(program1.getProgramId());
program.setProgramName("blah");
program.setProgramDesc("ssup");
session.saveOrUpdate(program);
Phuoc
0
 
LVL 1

Author Comment

by:aturetsky
ID: 16882992
well, in my case, that's the whole problem - the program doesn't know which time is first and which isn't until it looks to see if it's already in the database.  Now, of course, I can try to do a find by that program_name first and see if it's there - but that's exactly what I was trying to avoid.

Having said that, I am curious why you would recommend a merge rather than a find by program name.  What would be the goal of merge here?
0
 
LVL 6

Accepted Solution

by:
phuocnh earned 250 total points
ID: 16883044
OK!
find is a solution but merge is simpler than find because of  giving the condition.
merge will look up in the db if the object is not found, it save the object into db and return the persistent object.
(note that in your case if the object aldready exists in the DB, merge also save into the DB and exception will occur because of their identifiers)
Phuoc
 
0
 
LVL 1

Author Comment

by:aturetsky
ID: 16905717
note to the reader:
while the previous post is marked as accepted due to its general usefulness, please note that there does not appear to be a built-in way to drive insert-update behavior based on the value of non-key field, even if it's a candidate, natural key.
0

Featured Post

DevOps Toolchain Recommendations

Read this Gartner Research Note and discover how your IT organization can automate and optimize DevOps processes using a toolchain architecture.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

In this post we will learn different types of Android Layout and some basics of an Android App.
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
This tutorial covers a practical example of lazy loading technique and early loading technique in a Singleton Design Pattern.
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
Suggested Courses

739 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question