Using saveOrUpdate() in Hibernate creates new records instead of updating existing ones

76,729

Solution 1

This code always trying insert bill to database , rather than update when row about Bill exists in DB...

From the section 10.7. Automatic state detection of the Hibernate core documentation:

saveOrUpdate() does the following:

  • if the object is already persistent in this session, do nothing
  • if another object associated with the session has the same identifier, throw an exception
  • if the object has no identifier property, save() it
  • if the object's identifier has the value assigned to a newly instantiated object, save() it
  • if the object is versioned by a <version> or <timestamp>, and the version property value is the same value assigned to a newly instantiated object, save() it
  • otherwise update() the object

When you do:

User bill = new User();
bill.setName("Bill");
session.saveOrUpdate(bill);

This newly created instance does not have any identifier value assigned and saveOrUpdate() will save() it, as documented. If this is not what you want, make the name the primary key.

Solution 2

So you want:

User u1 = new User("Bill");
session.save(u1);
User u2 = new User("Bill");
session.saveOrUpdate(u2);

to notice that u1 and u2 are the same Bill and only store it once? Hibernate has no way of knowing that the Bills are the same person. It only looks at the id of User u2, sees that it's not set, concludes that u2 should be inserted, tries that and reports an exception.

SaveOrUpdate persists both a completely new object and a loaded object that's currently attached to the session. So this works (assuming you have a findByName method somewhere and there's another attribute, let's say favoriteColor):

User u1 = new User("Bill");
u1.setFavoriteColor("blue");
session.saveOrUpdate(u1);
User u2 = findByName("Joe");
u2.setFavoriteColor("red");
session.saveOrUpdate(u2);

But that's not what you want, right?

Your problem is made worse by your entity having a surrogate primary key and, separately, a business key (enforced via a uniqueness constraint). If you didn't have an id field and only name as primary key, you could use merge() (for a discussion of saveOrUpdate vs merge, look here):

User u1 = new User("Bill");
u1.setFavoriteColor("blue");
session.save(u1);
User u2 = new User("Bill");
u2.setFavoriteColor("red");
u2 = session.merge(u2);

But you don't have that, you need to enforce both the PK constraint on id and the uniqueness on name. So you'll need some variation of merge that does that. Very roughly, you'd like something along these lines:

public User mergeByName(User user) {
    User merged;
    User candidate = findByName(user.getName());
    if (candidate != null) {
        user.setId(candidate.getId());
        merged = session.merge(user);
    } else {
        session.save(user);
        merged = user;
    }
    return merged;
}

Solution 3

So, Id is not saved in the database ?

Why is Id not the primary key in de DB, if you map it in Hibernate as being the identifier ?

Why haven't you just put a unique constraint on 'Name' in the database, and create a Primary Key constraint on Id ?

Solution 4

First question: If you need to fetch a user named "Bill". How would you do that? That should give you an idea.

To update, you need to have an identity associated with the user object. Having just the name attribute set is not going to help. If you want to update regardless without having the identifier, use HQL as query.

Query query = session.createQuery("update User u set u.name = :newName where u.name=:name");
query.setString("name", "Bill");
query.setString("newName", "John");
query.executeUpdate();
Share:
76,729
kunkanwan
Author by

kunkanwan

Updated on June 14, 2020

Comments

  • kunkanwan
    kunkanwan almost 4 years

    I have a class User

    class User { 
      int id;
      String name;
    }
    

    where id is native generator in User.hbm.xml and name is primary-key in DB.

    In my database I saved some information about Users.

    Than I want to connect with this information about User.

    For example in my DB I have a row INSERT INTO User VALUES ('Bill');

    Main.java

    User bill = new User();
    bill.setName("Bill");
    session.saveOrUpdate(bill);
    

    This code always tries to insert a new Bill row into the table rather than update the existing Bill row.

  • kunkanwan
    kunkanwan almost 14 years
    Thx for your rely. id is primary key and name has unique constriant We have object class User for instance : User bill = new User() and attributes about bill we get from keybord or website , and my question is: How can you make saveOrUpdate() meaning if bill.name exists in db we call update() else we save bill.
  • kunkanwan
    kunkanwan almost 14 years
    Yeah , you are right but I want function saveOrUpdate() from hibernate would do the work for me. I could write my saveOrUpdate() where i check in select if name exists in db and call save() or update(). I think it's not good idea , and maybe hibernate has better solution for example override equals() where we compare element from bussines key.
  • kunkanwan
    kunkanwan almost 14 years
    Thx for your rely , I see why my program always trying insert. How can I do that without making name the primary key because I have id which is faster and easier. I can do it using extra select like that: User bill = new User(); bill.setName("Bill"); bill.setId(func()); where func() is a function which result is null when row with name='Bill' doesn't exists otherwise return id number this row; How can I do that without select ? How solve this problem Pro Developer ?
  • Devanshu Mevada
    Devanshu Mevada almost 14 years
    @kunkanwan: I'm not sure how I should understand this "pro developer". Right now, this doesn't motivate me to help you further.
  • kunkanwan
    kunkanwan almost 14 years
    I'm afraid you don't understand me. My english is poor. In sentence 'Pro developer' I want know how solve this in software industry( I'm student without experience) where my simple function with extra select is not enough good. I'm sorry if I hurt you.
  • Devanshu Mevada
    Devanshu Mevada almost 14 years
    @kunkanwan Ok, no problem then. I'll come back on this question tomorrow.
  • J888
    J888 almost 11 years
    @PascalThivent but if you make name as PK, you may later need to have it as FK of another table then you would have many names in new table which gets large amount of the DB in compare to using an ID as PK