简体   繁体   中英

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

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.

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.

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;
}

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 ?

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();

您在更新时应使用session.update(object),在保存时应使用session.save(object)

If name field is primary key. Then if you set the same name more than once, how it will create new record. ?may be you are not understanding the concept right.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM