Qual é a diferença entre session.Merge e session.SaveOrUpdate?

86

Percebo que às vezes com meus objetos pai / filho ou relacionamentos muitos para muitos, preciso chamar SaveOrUpdateou Merge. Normalmente, quando preciso chamar SaveOrUpdate, a exceção que recebo ao chamar Mergetem a ver com objetos temporários não sendo salvos primeiro.

Explique a diferença entre os dois.

EvilSyn
fonte

Respostas:

157

Isso é da seção 10.7. Detecção automática de estado da documentação de referência do Hibernate:

saveOrUpdate () faz o seguinte:

  • se o objeto já é persistente nesta sessão, não faça nada
  • se outro objeto associado à sessão tiver o mesmo identificador, lance uma exceção
  • se o objeto não tem propriedade identificadora, save () it
  • se o identificador do objeto tiver o valor atribuído a um objeto recém-instanciado, salve ()
  • se o objeto for versionado (por uma <version> ou <timestamp>), e o valor da propriedade da versão for o mesmo valor atribuído a um objeto recém-instanciado, salve ()
  • caso contrário, update () o objeto

e merge () é muito diferente:

  • se houver uma instância persistente com o mesmo identificador atualmente associado à sessão, copie o estado do objeto fornecido para a instância persistente
  • se não houver uma instância persistente atualmente associada à sessão, tente carregá-la do banco de dados ou crie uma nova instância persistente
  • a instância persistente é retornada
  • a instância dada não se torna associada à sessão, ela permanece separada

Você deve usar Merge () se estiver tentando atualizar objetos que em um ponto foram desconectados da sessão, especialmente se houver instâncias persistentes desses objetos atualmente associados à sessão. Caso contrário, usar SaveOrUpdate () nesse caso resultaria em uma exceção.

David Crow
fonte
boa resposta ... Eu me pergunto - se eu usar merge em uma nova entidade, há alguma razão para usar save afterwords ou posso assumir que merge criou a nova entidade no banco de dados com certeza? (e se for uma entidade separada, após a fusão as alterações são omitidas no banco de dados automaticamente?)
Dani
5
Você tem certeza disso? Olhando para a fonte NHiberante SaveOrUpdateCopy aciona um evento Merge com os mesmos parâmetros da função Merge. Eu acho que eles são idênticos, a função SaveOrUpdateCopy é algo que existia em hibernate / nhibernate desde 1.0. A função Merge é nova e foi adicionada para hibernar para estar em conformidade com um novo padrão java (eu acho)
Torkel
5
@Torkel - SaveOrUpdateCopynão é o mesmo que SaveOrUpdate. Não tenho certeza se o questionador queria comparar Mergeo primeiro ou o último. SaveOrUpdateCopyé um método obsoleto que fez uma fusão no NHibernate antes de Mergeser importado.
codekaizen
bom saber ... SaveOrUpdate ainda é muito usado em tutoriais.
anael
9

Pelo que entendi, merge()pegarei um objeto que não pode estar associado à sessão atual e copiará seu estado (valores de propriedade, etc.) para um objeto que está associado à sessão atual (com o mesmo valor / identificador de PK, de curso).

saveOrUpdate()irá chamar Salvar ou Atualizar em sua sessão, com base no valor de identidade de um determinado objeto.

Ryan Duffield
fonte
4

SaveOrUpdateCopy()agora está obsoleto a partir do NHibernate 3.1. Merge()deve ser usado em seu lugar.

Ricardo Peres
fonte
9
É o SaveOrUpdateCopyque está marcado Obsolete, não SaveOrUpdate. Parece haver muita confusão entre esses dois métodos diferentes nesta pergunta e nas respostas subsequentes.
codekaizen
2
** Update()**

: - se você tiver certeza de que a sessão não contém uma instância já persistente com o mesmo identificador, use update para salvar os dados no hibernar

** Merge()**

: -se você deseja salvar suas modificações a qualquer momento sem saber sobre o estado de uma sessão, então use merge () para hibernar.

Mohit Singh
fonte
1

Encontrei este link que explicou muito bem esse tipo de exceção:

O que funcionou para mim é o seguinte:

  1. No arquivo de mapeamento Myclass.hbm.xml, defina cascade="merge"
  2. SaveOrUpdate o objeto filho / dependente antes de atribuí-lo ao objeto pai.
  3. SaveOrUpdate o objeto pai.

No entanto, esta solução tem limitações. ou seja, você deve cuidar para salvar seu filho / objeto dependente em vez de deixar que o hibernate faça isso por você.

Se alguém tiver uma solução melhor, eu gostaria de ver.

Quoc Truong
fonte
-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
fonte
2
Você deve considerar editar sua resposta para mostrar o código que foi afetado e, então, talvez considerar o despejo completo do código no final. Do jeito que está, temos que rolar para baixo e mudar os comentários. Veja como responder .
Bugs