Jaka jest różnica między session.Merge i session.SaveOrUpdate?

87

Czasami zauważam, że w przypadku moich obiektów rodzic / dziecko lub relacji wiele-do-wielu, muszę zadzwonić albo SaveOrUpdatealbo Merge. Zwykle, gdy muszę zadzwonić SaveOrUpdate, wyjątek, który otrzymuję podczas wywoływania, dotyczy Mergetego, że obiekty przejściowe nie są zapisywane jako pierwsze.

Proszę wyjaśnić różnicę między nimi.

EvilSyn
źródło

Odpowiedzi:

158

To pochodzi z sekcji 10.7. Automatyczne wykrywanie stanu w dokumentacji referencyjnej hibernacji:

saveOrUpdate () wykonuje następujące czynności:

  • jeśli obiekt jest już trwały w tej sesji, nie rób nic
  • jeśli inny obiekt powiązany z sesją ma ten sam identyfikator, zgłoś wyjątek
  • jeśli obiekt nie ma właściwości identyfikatora, zapisz () go
  • jeśli identyfikator obiektu ma wartość przypisaną do nowo utworzonego obiektu, zapisz () go
  • jeśli obiekt jest wersjonowany (przez <version> lub <timestamp>), a wartość właściwości wersji jest tą samą wartością przypisaną do nowo utworzonego obiektu, zapisz () go
  • w przeciwnym razie update () obiektu

a merge () jest bardzo różna:

  • jeśli istnieje trwała instancja o tym samym identyfikatorze aktualnie powiązana z sesją, skopiuj stan danego obiektu do trwałej instancji
  • jeśli nie ma aktualnie skojarzonej z sesją instancji trwałej, spróbuj ją załadować z bazy danych lub utwórz nową instancję trwałą
  • zwracana jest trwała instancja
  • dana instancja nie zostaje skojarzona z sesją, pozostaje odłączona

Powinieneś użyć Merge (), jeśli próbujesz zaktualizować obiekty, które w pewnym momencie zostały odłączone od sesji, zwłaszcza jeśli mogą istnieć trwałe instancje tych obiektów aktualnie skojarzonych z sesją. W przeciwnym razie użycie SaveOrUpdate () w takim przypadku spowodowałoby wyjątek.

David Crow
źródło
dobra odpowiedź ... Zastanawiam się - jeśli używam merge na nowej encji, czy jest jakiś powód, aby używać save afterwords, czy też mogę założyć, że merge na pewno utworzył nową jednostkę w DB? (a jeśli jest to jednostka odłączona, czy po scaleniu zmiany w bazie danych są automatycznie pomijane?)
Dani,
5
Jesteś tego pewien? Patrząc na źródło NHiberante SaveOrUpdateCopy wyzwala zdarzenie Merge o takich samych parametrach jak funkcja Merge. Myślę, że są one identyczne, funkcja SaveOrUpdateCopy jest czymś, co istnieje w hibernacji / NHibernate od 1,0 funkcja Merge jest nowy i dodano do hibernacji w celu dostosowania do nowego standardu Java (myślę)
Torkel
5
@Torkel - SaveOrUpdateCopyto nie to samo co SaveOrUpdate. Nie jestem pewien, czy pytający chciał porównać Mergeto pierwsze, czy drugie. SaveOrUpdateCopyjest przestarzałą metodą, która przed Mergeimportem dokonała połączenia z NHibernate .
codekaizen
dobrze wiedzieć ... SaveOrUpdate jest nadal często używany w samouczkach.
anael
9

Jak rozumiem, merge()weźmie obiekt, który może nie być powiązany z bieżącą sesją i skopiuje jego stan (wartości właściwości itp.) Do obiektu, który jest powiązany z bieżącą sesją (z tą samą wartością PK / identyfikatorem, kierunek).

saveOrUpdate()wywoła Save lub Update w Twojej sesji na podstawie wartości tożsamości danego obiektu.

Ryan Duffield
źródło
4

SaveOrUpdateCopy()jest obecnie przestarzałe od NHibernate 3.1. Merge()należy użyć zamiast tego.

Ricardo Peres
źródło
9
To SaveOrUpdateCopyjest zaznaczone Obsolete, a nie SaveOrUpdate. Wydaje się, że w tym pytaniu i kolejnych odpowiedziach istnieje wiele nieporozumień między tymi dwiema różnymi metodami.
codekaizen
2
** Update()**

: - jeśli masz pewność, że sesja nie zawiera już trwałej instancji o tym samym identyfikatorze, użyj aktualizacji, aby zapisać dane w hibernacji

** Merge()**

: -jeśli chcesz zapisać swoje modyfikacje w dowolnym momencie, nie wiedząc o stanie sesji, użyj funkcji merge () w trybie hibernacji.

Mohit Singh
źródło
1

Znalazłem ten link, który wykonał całkiem niezłą robotę, wyjaśniając tego typu wyjątek:

Dla mnie zadziałało:

  1. W mapowaniu pliku Myclass.hbm.xml ustaw cascade="merge"
  2. SaveOrUpdate obiekt podrzędny / zależny najpierw przed przypisaniem go do obiektu nadrzędnego.
  3. SaveOrUpdate obiekt nadrzędny.

Jednak to rozwiązanie ma ograniczenia. tj. musisz zadbać o uratowanie swojego dziecka / obiektu zależnego, zamiast pozwolić na hibernację robiąc to za Ciebie.

Jeśli ktoś ma lepsze rozwiązanie, chciałbym zobaczyć.

Quoc Truong
źródło
-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();
        }

    }
}
Deepak
źródło
2
Powinieneś rozważyć edycję swojej odpowiedzi, aby pokazać kod, który jest wykonywany, a następnie może rozważyć pełny zrzut kodu na końcu. W tej chwili musimy przewinąć w dół i przypadkowo w komentarzach. Zobacz, jak odpowiedzieć .
Bugs