¿Cuál es la diferencia entre session.Merge y session.SaveOrUpdate?

86

A veces me doy cuenta de que con mis objetos padre / hijo o relaciones de muchos a muchos, necesito llamar a SaveOrUpdateo Merge. Por lo general, cuando necesito llamar SaveOrUpdate, la excepción que obtengo al llamar Mergetiene que ver con que los objetos transitorios no se guarden primero.

Explique la diferencia entre los dos.

EvilSyn
fuente

Respuestas:

157

Esto es de la sección 10.7. Detección automática del estado de la documentación de referencia de Hibernate:

saveOrUpdate () hace lo siguiente:

  • si el objeto ya es persistente en esta sesión, no haga nada
  • si otro objeto asociado con la sesión tiene el mismo identificador, lanza una excepción
  • si el objeto no tiene propiedad de identificador, guárdelo ()
  • si el identificador del objeto tiene el valor asignado a un objeto recién instanciado, guárdelo ()
  • si el objeto está versionado (por una <version> o <timestamp>), y el valor de la propiedad de versión es el mismo valor asignado a un objeto recién instanciado, guárdelo ()
  • de lo contrario, actualice () el objeto

y merge () es muy diferente:

  • si hay una instancia persistente con el mismo identificador asociado actualmente con la sesión, copie el estado del objeto dado en la instancia persistente
  • Si no hay una instancia persistente actualmente asociada con la sesión, intente cargarla desde la base de datos o cree una nueva instancia persistente.
  • se devuelve la instancia persistente
  • la instancia dada no se asocia con la sesión, permanece separada

Debe usar Merge () si está intentando actualizar objetos que en un momento se separaron de la sesión, especialmente si puede haber instancias persistentes de esos objetos actualmente asociados con la sesión. De lo contrario, usar SaveOrUpdate () en ese caso resultaría en una excepción.

David Crow
fuente
buena respuesta ... Me pregunto: si utilizo la combinación en una nueva entidad, ¿hay alguna razón para usar guardar las palabras posteriores o puedo asumir que la combinación ha creado la nueva entidad en la base de datos? (y si es una entidad separada, una vez que se fusionan, ¿se omiten los cambios en la base de datos automáticamente?)
Dani
5
¿Estas seguro acerca de esto? Al mirar la fuente SaveOrUpdateCopy de NHiberante, se activa un evento Merge con los mismos parámetros que la función Merge. Creo que son idénticos, la función SaveOrUpdateCopy es algo que ha existido en hibernate / nhibernate desde 1.0, la función Merge es nueva y se agregó a hibernate para cumplir con un nuevo estándar java (creo)
Torkel
5
@Torkel: SaveOrUpdateCopyno es lo mismo que SaveOrUpdate. No estoy seguro de si el interrogador quería comparar Mergeel primero o el segundo. SaveOrUpdateCopyes un método ahora obsoleto que se fusionó en NHibernate antes de Mergeser importado.
codekaizen
es bueno saber ... SaveOrUpdate todavía se usa mucho en tutoriales.
anael
9

Según tengo entendido, merge()tomará un objeto que puede no estar asociado con la sesión actual y copiará su estado (valores de propiedad, etc.) a un objeto que sea asociado con la sesión actual (con el mismo valor / identificador PK, de curso).

saveOrUpdate()llamará a Guardar o Actualizar en su sesión, según el valor de identidad de un objeto determinado.

Ryan Duffield
fuente
4

SaveOrUpdateCopy()ahora está obsoleto a partir de NHibernate 3.1. Merge()debe usarse en su lugar.

Ricardo Peres
fuente
9
Es lo SaveOrUpdateCopyque está marcado Obsolete, no SaveOrUpdate. Parece haber mucha confusión entre estos dos métodos diferentes en esta pregunta y las respuestas posteriores.
codekaizen
2
** Update()**

: - si está seguro de que la sesión no contiene una instancia ya persistente con el mismo identificador, use la actualización para guardar los datos en hibernación

** Merge()**

: -si desea guardar sus modificaciones en cualquier momento sin conocer el estado de una sesión, utilice merge () en hibernación.

Mohit Singh
fuente
1

Encontré esto enlace que hizo un buen trabajo al explicar este tipo de excepción:

Lo que funcionó para mí es lo siguiente:

  1. En el archivo de asignación Myclass.hbm.xml, establezca cascade="merge"
  2. SaveOrUpdate el objeto hijo / dependiente primero antes de asignarlo al objeto padre.
  3. SaveOrUpdate el objeto padre.

Sin embargo, esta solución tiene limitaciones. es decir, tienes que encargarte de salvar a tu hijo / objeto dependiente en lugar de dejar que hibernar lo haga por ti.

Si alguien tiene una mejor solución, me gustaría verla.

Quoc Truong
fuente
-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
fuente
2
Debería considerar editar su respuesta para mostrar el código que se efectúa y luego quizás considerar el volcado de código completo al final. Tal como está, tenemos que desplazarnos hacia abajo y pasar el rato en los comentarios. Vea cómo responder .
Bugs