session.Merge 和 session.SaveOrUpdate 有什么区别?(涉及IT技术)

89

我注意到在我的父子对象或多对多关系中,有时需要调用SaveOrUpdateMerge。通常情况下,当我需要调用SaveOrUpdate时,调用Merge时出现的异常与瞬态对象未被首先保存有关。

请解释二者之间的区别。

6个回答

161
这是来自Hibernate参考文档10.7. 自动状态检测部分的内容:

saveOrUpdate() 执行以下操作:

  • 如果对象已经在此会话中持久化,则不执行任何操作
  • 如果与会话关联的另一个对象具有相同的标识符,则抛出异常
  • 如果对象没有标识属性,则将其保存(save())
  • 如果对象的标识符具有分配给新实例化对象的值,则将其保存(save())
  • 如果对象已被版本化(通过<version>或<timestamp>),并且版本属性值与分配给新实例化对象的值相同,则将其保存(save())
  • 否则,更新(update())该对象

而 merge() 则非常不同:

  • 如果当前与会话关联的持久化实例具有相同的标识符,则将给定对象的状态复制到持久化实例上
  • 如果当前未关联持久化实例,则尝试从数据库加载它,或创建新的持久化实例
  • 返回持久化实例
  • 给定实例不会与会话关联,它仍然是游离的

如果您正在尝试更新一些曾经与会话分离的对象,特别是那些可能与会话关联的持久化实例,请使用 Merge()。否则,在这种情况下使用 SaveOrUpdate() 将导致异常。


好的回答...我想知道 - 如果我在一个新实体上使用合并,是否有任何理由在之后使用保存,或者我可以假设合并已经确保在数据库中创建了新实体?(如果它是分离的实体,一旦合并,更改是否自动省略到数据库中?) - Dani
5
你确定这个吗?看NHiberante源代码,SaveOrUpdateCopy函数会触发一个与Merge函数参数相同的Merge事件。我认为它们是相同的,SaveOrUpdateCopy函数自Hibernate/NHibernate 1.0版本就存在了,而Merge函数是新添加到Hibernate中以符合新的Java标准(我想)。 - Torkel
5
@Torkel - SaveOrUpdateCopy不同于SaveOrUpdate。我不确定提问者想比较哪个是先前的方法,是Merge还是前者或后者。SaveOrUpdateCopy是一个已过时的方法,在引入Merge之前,在NHibernate中执行合并操作。 - codekaizen
很好知道... SaveOrUpdate 在教程中仍然被广泛使用。 - anael

9
据我理解,merge()方法可以将一个可能与当前会话无关的对象的状态(属性值等)复制到与当前会话相关联的对象上(当然,它们具有相同的PK值/标识符)。 saveOrUpdate()方法将根据给定对象的标识值,在会话上调用SaveUpdate

4

SaveOrUpdateCopy()在NHibernate 3.1中已被弃用,应使用Merge()代替。


9
标记为“Obsolete”的是SaveOrUpdateCopy而不是SaveOrUpdate。在这个问题和后续的回答中,似乎有很多人混淆了这两种不同的方法。 - codekaizen

2
** Update()**

如果您确定会话中不包含具有相同标识符的已持久化实例,则可以使用update在Hibernate中保存数据。

** Merge()**

如果您想在不知道会话状态的情况下随时保存修改,则可以在Hibernate中使用merge()。


1

我找到了this一个链接,它解释了这种异常的原因:

对我有用的方法如下:

  1. 在映射文件Myclass.hbm.xml中设置cascade="merge"
  2. 先保存或更新子对象/依赖对象,然后再将其分配给父对象。
  3. 保存或更新父对象。

但是,这种解决方案有局限性。也就是说,你必须注意保存你的子对象/依赖对象,而不是让Hibernate为你完成。

如果有更好的解决方案,我想看看。


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

    }
}

2
你应该考虑编辑你的回答,展示受影响的代码,然后可能在最后考虑完整的代码转储。目前我们必须向下滚动并偶然看到评论。请参见[答案]。 - Bugs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接