У чому різниця між session.Merge і session.SaveOrUpdate?


86

Я помічаю, що іноді з моїми батьками / дочірніми об’єктами або стосунками «багато-до-багатьох» мені потрібно зателефонувати SaveOrUpdateабо Merge. Зазвичай, коли мені потрібно зателефонувати SaveOrUpdate, виняток, який я отримую при дзвінку Merge, пов’язаний з тимчасовими об’єктами, які не зберігаються першими.

Поясніть, будь ласка, різницю між ними.

Відповіді:


157

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

saveOrUpdate () робить наступне:

  • якщо об’єкт уже постійний у цьому сеансі, нічого не робити
  • якщо інший об'єкт, асоційований із сеансом, має такий самий ідентифікатор, викиньте виняток
  • якщо об'єкт не має властивості ідентифікатора, збережіть () його
  • якщо ідентифікатор об'єкта має значення, присвоєне новоствореному об'єкту, збережіть () його
  • якщо об'єкт має версію (за допомогою <version> або <timestamp>), а значення властивості версії має те саме значення, що присвоюється новоствореному об'єкту, збережіть () його
  • в іншому випадку оновіть () об'єкт

і merge () дуже різний:

  • якщо існує постійний екземпляр з тим самим ідентифікатором, який наразі асоційований із сеансом, скопіюйте стан даного об'єкта на постійний екземпляр
  • якщо в даний час з сеансом не пов’язаний постійний екземпляр, спробуйте завантажити його з бази даних або створити новий постійний екземпляр
  • повертається постійний екземпляр
  • даний екземпляр не стає асоційованим із сеансом, він залишається відокремленим

Вам слід використовувати Merge (), якщо ви намагаєтесь оновити об'єкти, які в один момент були від'єднані від сеансу, особливо якщо можуть бути постійні екземпляри тих об'єктів, які в даний час пов'язані з сеансом. В іншому випадку використання SaveOrUpdate () у цьому випадку призведе до винятку.


хороша відповідь ... Цікаво - якщо я використовую злиття на новій сутності, чи є якась причина використовувати збережені післяслова або я можу припустити, що злиття точно створило нову сутність в БД? (а якщо це відокремлена сутність, після об’єднання зміни в базі даних пропускаються автоматично?)
Дані

5
Ви впевнені в цьому? Перегляд джерела NHiberante SaveOrUpdateCopy ініціює подію Merge з тими ж параметрами, що і функція Merge. Я думаю, що вони ідентичні, функція SaveOrUpdateCopy - це те, що існує в режимі глибокого сну / нгібернації з 1.0, функція Merge є новою і була додана до режиму глибокого сну відповідно до нового стандарту Java (я думаю)
Торкель,

5
@Torkel - SaveOrUpdateCopyце не те саме, що SaveOrUpdate. Я не впевнений, чи запитувач хотів порівняти Mergeпершого чи другого. SaveOrUpdateCopyє застарілим методом, який зробив злиття NHibernate до Mergeімпорту.
codekaizen

корисно знати ... SaveOrUpdate все ще активно використовується в підручниках.
анаел

9

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

saveOrUpdate()викличе Зберегти або Оновити під час сеансу на основі значення ідентичності даного об'єкта.


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
Вам слід подумати про редагування своєї відповіді, щоб показати код, який виконується, а потім, можливо, розглянути повний дамп коду в кінці. У теперішньому вигляді ми маємо прокручувати вниз і шанс коментувати коментарі. Див. Як відповісти .
Багс,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.