What's the difference between session.Merge and session.SaveOrUpdate?
Asked Answered
S

6

89

I notice sometimes with my parent/child objects or many-to-many relationships, I need to call either SaveOrUpdate or Merge. Usually, when I need to call SaveOrUpdate, the exception I get on calling Merge has to do with transient objects not being saved first.

Please explain the difference between the two.

Servant answered 4/10, 2008 at 20:40 Comment(0)
S
161

This is from section 10.7. Automatic state detection of the Hibernate Reference 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

and merge() is very different:

  • if there is a persistent instance with the same identifier currently associated with the session, copy the state of the given object onto the persistent instance
  • if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance
  • the persistent instance is returned
  • the given instance does not become associated with the session, it remains detached

You should use Merge() if you are trying to update objects that were at one point detached from the session, especially if there might be persistent instances of those objects currently associated with the session. Otherwise, using SaveOrUpdate() in that case would result in an exception.

Shreveport answered 4/10, 2008 at 21:20 Comment(3)
good answer... I wonder - if I use merge on a new entity is there any reason to use save afterwords or I can assume merge has created the new entity in the DB for sure ? (and if it's an detached entity, once merge is the changes omitted to the DB automatically ?)Sergu
Are you sure about this? Looking at the NHiberante source SaveOrUpdateCopy triggers a Merge event with the same parameters as the Merge function. I think the they are identical, the SaveOrUpdateCopy function is something that has existed in hibernate/nhibernate since 1.0 the Merge function is new and was added to to hibernate to conform to a new java standard (I think)Rarefy
@Rarefy - SaveOrUpdateCopy isn't the same as SaveOrUpdate. I'm not sure if the questioner wanted to compare Merge the former or the latter. SaveOrUpdateCopy is a now-obsolete method which did a merge in NHibernate prior to Merge being imported.Tricornered
O
10

As I understand it, merge() will take an object that may not be associated with the current session, and copy its state (property values, etc.) to an object that is associated with the current session (with the same PK value/identifier, of course).

saveOrUpdate() will call Save or Update on your session, based on a given object's identity value.

Orthopedic answered 4/10, 2008 at 21:23 Comment(0)
H
4

SaveOrUpdateCopy() is now deprecated as of NHibernate 3.1. Merge() should be used instead.

Hutner answered 5/4, 2011 at 11:38 Comment(1)
It's SaveOrUpdateCopy which is marked Obsolete, not SaveOrUpdate. There appears to be a lot of confusion between these two different methods in this question and subsequent answers.Tricornered
E
2
** Update()**

:- if you are sure that the session does not contains an already persistent instance with the same identifier then use update to save the data in hibernate

** Merge()**

:-if you want to save your modifications at any time with out knowing about the state of an session then use merge() in hibernate.

Esther answered 20/4, 2015 at 9:13 Comment(0)
M
1

I found this link that did a pretty good job explaining this type of exception:

What worked for me is the following:

  1. In the mapping Myclass.hbm.xml file, set cascade="merge"
  2. SaveOrUpdate the child/dependent object first before assigning it to the parent object.
  3. SaveOrUpdate the parent object.

However, this solution has limitations. i.e., you have to take care of saving your child/dependent object instead of letting hibernate doing that for you.

If anyone has a better solution, I'd like to see.

Melissamelisse answered 17/2, 2014 at 21:23 Comment(0)
A
-2
@Entity
@Table(name="emp")
public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="emp_id")
    private int id;
    @Column(name="emp_name")
    private String name;
    @Column(name="salary")
    private int Salary;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return Salary;
    }

    public void setSalary(int salary) {
        this.Salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

public enum HibernateUtil {
    INSTANCE;
    HibernateUtil(){
        buildSessionFactory();
    }
    private SessionFactory sessionFactory=null;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    private  void buildSessionFactory() {
        Configuration configuration = new Configuration();

        configuration.addAnnotatedClass (TestRefresh_Merge.Employee.class);
        configuration.setProperty("connection.driver_class","com.mysql.jdbc.Driver");
        configuration.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/hibernate");                                
        configuration.setProperty("hibernate.connection.username", "root");     
        configuration.setProperty("hibernate.connection.password", "root");
        configuration.setProperty("dialect", "org.hibernate.dialect.MySQLDialect");
        configuration.setProperty("hibernate.hbm2ddl.auto", "update");
        configuration.setProperty("hibernate.show_sql", "true");
        configuration.setProperty(" hibernate.connection.pool_size", "10");
        /* configuration.setProperty(" hibernate.cache.use_second_level_cache", "true");
         configuration.setProperty(" hibernate.cache.use_query_cache", "true");
         configuration.setProperty(" cache.provider_class", "org.hibernate.cache.EhCacheProvider");
         configuration.setProperty("hibernate.cache.region.factory_class" ,"org.hibernate.cache.ehcache.EhCacheRegionFactory");
        */
        // configuration
        StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
           sessionFactory = configuration.buildSessionFactory(builder.build());
           setSessionFactory(sessionFactory);
    }

    public  static SessionFactory getSessionFactoryInstance(){
        return INSTANCE.getSessionFactory();
    }
} 


public class Main {
    public static void main(String[] args) {
        HibernateUtil util=HibernateUtil.INSTANCE;
        SessionFactory factory=util.getSessionFactory();
        //save(factory); 
        retrieve(factory);
    }

     private static void retrieve(SessionFactory factory) {
        Session sessionOne=factory.openSession();

        Employee employee=(Employee)sessionOne.get(Employee.class, 5);

        sessionOne.close(); // detached Entity

        employee.setName("Deepak1");

        Session sessionTwo=factory.openSession();

        Employee employee1=(Employee)sessionTwo.get(Employee.class, 5);
        sessionTwo.beginTransaction();
        sessionTwo.saveOrUpdate(employee); // it will throw exception

        //sessionTwo.merge(employee); // it will work

        sessionTwo.getTransaction().commit();

        sessionTwo.close();

    }

    private static void save(SessionFactory factory) {
        Session sessionOne=factory.openSession();
        Employee emp=new Employee();
        emp.setName("Abhi");
        emp.setSalary(10000);
        sessionOne.beginTransaction();
        try{

            sessionOne.save(emp);
            sessionOne.getTransaction().commit();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            sessionOne.close();
        }

    }
}
Andraandrade answered 6/3, 2016 at 18:24 Comment(1)
You should consider editing your answer to show the code that is effected and then maybe consider the full code dump at the end. As it stands we have to scroll down and chance across the comments. See How to Answer.Tulipwood

© 2022 - 2024 — McMap. All rights reserved.