В чем разница между session.Merge и session.SaveOrUpdate?

87

Иногда я замечаю, что с моими родительскими / дочерними объектами или отношениями многие-ко-многим мне нужно вызвать либо SaveOrUpdateили Merge. Обычно, когда мне нужно позвонить SaveOrUpdate, исключение, которое я получаю при вызове Merge, связано с тем, что временные объекты не сохраняются в первую очередь.

Пожалуйста, объясните разницу между ними.

EvilSyn
источник

Ответы:

158

Это из раздела 10.7. Автоматическое определение состояния справочной документации Hibernate:

saveOrUpdate () выполняет следующие действия:

  • если объект уже является постоянным в этом сеансе, ничего не делать
  • если другой объект, связанный с сеансом, имеет тот же идентификатор, генерировать исключение
  • если у объекта нет свойства идентификатора, save () его
  • если идентификатор объекта имеет значение, присвоенное вновь созданному объекту, save () его
  • если объект версируется (с помощью <version> или <timestamp>), а значение свойства версии совпадает с значением, присвоенным вновь созданному объекту, save () его
  • в противном случае update () объект

и merge () очень разные:

  • если существует постоянный экземпляр с тем же идентификатором, который в настоящее время связан с сеансом, скопируйте состояние данного объекта на постоянный экземпляр
  • если в данный момент нет постоянного экземпляра, связанного с сеансом, попробуйте загрузить его из базы данных или создать новый постоянный экземпляр
  • постоянный экземпляр возвращается
  • данный экземпляр не становится связанным с сеансом, он остается отключенным

Вам следует использовать Merge (), если вы пытаетесь обновить объекты, которые в какой-то момент были отключены от сеанса, особенно если могут существовать постоянные экземпляры тех объектов, которые в настоящее время связаны с сеансом. В противном случае использование SaveOrUpdate () в этом случае приведет к исключению.

Дэвид Кроу
источник
хороший ответ ... Интересно - если я использую слияние для новой сущности, есть ли причина для использования послесловия сохранения, или я могу предположить, что слияние точно создало новую сущность в БД? (а если это отдельная сущность, после слияния изменения автоматически пропадают в БД?)
Дани
5
Ты уверен насчет этого? При просмотре источника NHiberante SaveOrUpdateCopy запускает событие Merge с теми же параметрами, что и функция Merge. Я думаю, что они идентичны, функция SaveOrUpdateCopy - это то, что существует в hibernate / nhibernate с 1.0, функция Merge является новой и была добавлена ​​в спящий режим, чтобы соответствовать новому стандарту java (я думаю)
Торкель
5
@Torkel - SaveOrUpdateCopyэто не то же самое, что SaveOrUpdate. Я не уверен, хотел ли спрашивающий сравнить Mergeпервое или второе. SaveOrUpdateCopy- это уже устаревший метод, который перед Mergeимпортом был объединен в NHibernate .
codekaizen
полезно знать ... SaveOrUpdate все еще активно используется в учебных пособиях.
Anael
9

Насколько я понимаю, merge()будет принимать объект , который не может быть связан с текущей сессией, и скопировать свое состояние (значение свойств и т.д.) к объекту , который является связанным с текущей сессией (с тем же значением PK / идентификатором, из курс).

saveOrUpdate()вызовет Save или Update в вашем сеансе на основе значения идентификатора данного объекта.

Райан Даффилд
источник
4

SaveOrUpdateCopy()теперь устарела в NHibernate 3.1. Merge()следует использовать вместо этого.

Рикардо Перес
источник
9
Это то, SaveOrUpdateCopyчто отмечено Obsolete, а не SaveOrUpdate. Кажется, существует большая путаница между этими двумя разными методами в этом вопросе и последующих ответах.
codekaizen
2
** Update()**

: - если вы уверены, что сеанс не содержит уже постоянного экземпляра с тем же идентификатором, используйте обновление, чтобы сохранить данные в спящем режиме

** Merge()**

: -если вы хотите сохранить изменения в любое время, не зная о состоянии сеанса, используйте merge () в спящем режиме.

Мохит Сингх
источник
1

Я нашел эту ссылку, которая довольно хорошо объясняет этот тип исключения:

Для меня сработало следующее:

  1. В файле сопоставления Myclass.hbm.xml установите cascade="merge"
  2. SaveOrUpdate дочерний / зависимый объект, прежде чем назначать его родительскому объекту.
  3. SaveOrUpdate родительский объект.

Однако у этого решения есть ограничения. т.е. вы должны позаботиться о сохранении вашего дочернего / зависимого объекта вместо того, чтобы позволить спящему режиму делать это за вас.

Если у кого-то есть лучшее решение, я бы хотел посмотреть.

Куок Чыонг
источник
-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
Вам следует отредактировать свой ответ, чтобы показать выполненный код, а затем, возможно, рассмотреть полный дамп кода в конце. В его нынешнем виде мы должны прокрутить вниз и случайно просмотреть комментарии. См. Как ответить .
Ошибки