Qual è la differenza tra session.Merge e session.SaveOrUpdate?


86

A volte noto con i miei oggetti genitore / figlio o relazioni molti-a-molti, ho bisogno di chiamare SaveOrUpdateo Merge. Di solito, quando ho bisogno di chiamare SaveOrUpdate, l'eccezione che ottengo chiamando Mergeha a che fare con oggetti temporanei che non vengono salvati per primi.

Per favore, spiega la differenza tra i due.

Risposte:


157

Questo è dalla sezione 10.7. Rilevamento automatico dello stato della documentazione di riferimento di Hibernate:

saveOrUpdate () esegue le seguenti operazioni:

  • se l'oggetto è già persistente in questa sessione, non fare nulla
  • se un altro oggetto associato alla sessione ha lo stesso identificatore, lancia un'eccezione
  • se l'oggetto non ha proprietà identificatore, salvalo ()
  • se l'identificatore dell'oggetto ha il valore assegnato a un oggetto appena istanziato, save () esso
  • se l'oggetto ha una versione (da un <version> o <timestamp>) e il valore della proprietà version è lo stesso valore assegnato a un oggetto appena istanziato, save () it
  • altrimenti aggiorna () l'oggetto

e merge () è molto diverso:

  • se c'è un'istanza persistente con lo stesso identificatore attualmente associato alla sessione, copia lo stato dell'oggetto dato sull'istanza persistente
  • se non c'è nessuna istanza persistente attualmente associata alla sessione, prova a caricarla dal database o crea una nuova istanza persistente
  • viene restituita l'istanza persistente
  • l'istanza data non viene associata alla sessione, rimane scollegata

Dovresti usare Merge () se stai tentando di aggiornare oggetti che a un certo punto erano scollegati dalla sessione, specialmente se potrebbero esserci istanze persistenti di quegli oggetti attualmente associati alla sessione. Altrimenti, l'utilizzo di SaveOrUpdate () in quel caso provocherebbe un'eccezione.


buona risposta ... Mi chiedo: se uso merge su una nuova entità, c'è qualche motivo per usare save afterwords o posso presumere che merge abbia creato la nuova entità nel DB di sicuro? (e se si tratta di un'entità distaccata, una volta che si effettua l'unione le modifiche vengono omesse automaticamente al DB?)
Dani

5
Sei sicuro di questo? Guardando la fonte NHiberante SaveOrUpdateCopy attiva un evento Merge con gli stessi parametri della funzione Merge. Penso che siano identici, la funzione SaveOrUpdateCopy è qualcosa che esiste in hibernate / nhibernate dalla 1.0 la funzione Merge è nuova ed è stata aggiunta a hibernate per conformarsi a un nuovo standard java (credo)
Torkel

5
@Torkel - SaveOrUpdateCopynon è lo stesso di SaveOrUpdate. Non sono sicuro se l'interrogante volesse confrontare Mergeil primo o il secondo. SaveOrUpdateCopyè un metodo ormai obsoleto che ha eseguito un'unione in NHibernate prima di Mergeessere importato.
codekaizen

Buono a sapersi ... SaveOrUpdate è ancora ampiamente utilizzato nei tutorial.
anael

9

A quanto mi risulta, merge()si terrà un oggetto che non può essere associato con la sessione corrente, copiando i suoi (i valori delle proprietà, ecc) dello stato a un oggetto che è associato con la sessione corrente (con lo stesso valore di PK / identificativo, di corso).

saveOrUpdate()chiamerà Salva o Aggiorna nella tua sessione, in base al valore di identità di un dato oggetto.


4

SaveOrUpdateCopy()è ora deprecato a partire da NHibernate 3.1. Merge()dovrebbe essere usato invece.


9
È quello SaveOrUpdateCopyche è segnato Obsolete, no SaveOrUpdate. Sembra esserci molta confusione tra questi due diversi metodi in questa domanda e nelle risposte successive.
codekaizen

2
** Update()**

: - se sei sicuro che la sessione non contenga un'istanza già persistente con lo stesso identificatore, usa update per salvare i dati in ibernazione

** Merge()**

: -se vuoi salvare le tue modifiche in qualsiasi momento senza conoscere lo stato di una sessione, usa merge () in ibernazione.


1

Ho trovato questo collegamento che ha fatto un buon lavoro spiegando questo tipo di eccezione:

Quello che ha funzionato per me è il seguente:

  1. Nel file di mappatura Myclass.hbm.xml, impostare cascade="merge"
  2. SaveOrUpdate l'oggetto figlio / dipendente prima di assegnarlo all'oggetto padre.
  3. SaveOrUpdate l'oggetto genitore.

Tuttavia, questa soluzione presenta dei limiti. cioè, devi prenderti cura di salvare tuo figlio / oggetto dipendente invece di lasciare che ibernazione lo faccia per te.

Se qualcuno ha una soluzione migliore, mi piacerebbe vedere.


-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
Dovresti considerare di modificare la tua risposta per mostrare il codice che è stato effettuato e quindi forse considerare il dump completo del codice alla fine. Allo stato attuale, dobbiamo scorrere verso il basso e cercare tra i commenti. Vedi come rispondere .
Bug
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.