Quelle est la différence entre session.Merge et session.SaveOrUpdate?

86

Je remarque parfois qu'avec mes objets parent / enfant ou des relations plusieurs-à-plusieurs, je dois appeler soit SaveOrUpdateou Merge. Habituellement, lorsque je dois appeler SaveOrUpdate, l'exception que j'obtiens lors de l'appel Mergeconcerne les objets transitoires qui ne sont pas enregistrés en premier.

Veuillez expliquer la différence entre les deux.

EvilSyn
la source

Réponses:

157

Ceci est tiré de la section 10.7. Détection automatique de l'état de la documentation de référence Hibernate:

saveOrUpdate () effectue les opérations suivantes:

  • si l'objet est déjà persistant dans cette session, ne rien faire
  • si un autre objet associé à la session a le même identifiant, lever une exception
  • si l'objet n'a pas de propriété d'identifiant, enregistrez-le ()
  • si l'identifiant de l'objet a la valeur assignée à un objet nouvellement instancié, save () it
  • si l'objet est versionné (par une <version> ou <timestamp>) et que la valeur de la propriété de version est la même valeur affectée à un objet nouvellement instancié, save () it
  • sinon mettre à jour () l'objet

et merge () est très différent:

  • s'il existe une instance persistante avec le même identifiant actuellement associé à la session, copiez l'état de l'objet donné sur l'instance persistante
  • s'il n'y a pas d'instance persistante actuellement associée à la session, essayez de la charger à partir de la base de données ou créez une nouvelle instance persistante
  • l'instance persistante est retournée
  • l'instance donnée n'est pas associée à la session, elle reste détachée

Vous devez utiliser Merge () si vous essayez de mettre à jour des objets qui étaient à un moment donné détachés de la session, en particulier s'il peut y avoir des instances persistantes de ces objets actuellement associés à la session. Sinon, l'utilisation de SaveOrUpdate () dans ce cas entraînerait une exception.

David Crow
la source
bonne réponse ... Je me demande - si j'utilise la fusion sur une nouvelle entité, y a-t-il une raison d'utiliser save afterwords ou je peux supposer que la fusion a créé la nouvelle entité dans la base de données à coup sûr? (et s'il s'agit d'une entité détachée, une fois la fusion effectuée, les modifications sont-elles automatiquement omises dans la base de données?)
Dani
5
Es-tu sûr de ça? L'examen de la source NHiberante SaveOrUpdateCopy déclenche un événement Merge avec les mêmes paramètres que la fonction Merge. Je pense qu'ils sont identiques, la fonction SaveOrUpdateCopy est quelque chose qui existe dans hibernate / nhibernate depuis 1.0, la fonction Merge est nouvelle et a été ajoutée à la mise en veille prolongée pour se conformer à un nouveau standard java (je pense)
Torkel
5
@Torkel - SaveOrUpdateCopyn'est pas la même chose que SaveOrUpdate. Je ne sais pas si l'interlocuteur voulait comparer Mergele premier ou le second. SaveOrUpdateCopyest une méthode désormais obsolète qui a effectué une fusion dans NHibernate avant d' Mergeêtre importée.
codekaizen
bon à savoir ... SaveOrUpdate est encore largement utilisé dans les tutoriels.
anael
9

Si je comprends bien, merge()prendra un objet qui ne peut être associé à la session en cours, et copier son état ( la valeur des propriétés, etc.) à un objet qui est associé à la session en cours (avec la même valeur PK / identifiant, de cours).

saveOrUpdate() appellera Save ou Update sur votre session, en fonction de la valeur d'identité d'un objet donné.

Ryan Duffield
la source
4

SaveOrUpdateCopy()est désormais obsolète depuis NHibernate 3.1. Merge()devrait être utilisé à la place.

Ricardo Peres
la source
9
C'est ce SaveOrUpdateCopyqui est marqué Obsolete, non SaveOrUpdate. Il semble y avoir beaucoup de confusion entre ces deux méthodes différentes dans cette question et les réponses ultérieures.
codekaizen
2
** Update()**

: - si vous êtes sûr que la session ne contient pas une instance déjà persistante avec le même identifiant alors utilisez update pour sauvegarder les données en hibernation

** Merge()**

: -si vous voulez enregistrer vos modifications à tout moment sans connaître l'état d'une session, utilisez merge () en hibernation.

Mohit Singh
la source
1

J'ai trouvé ce lien qui a fait un très bon travail expliquant ce type d'exception:

Ce qui a fonctionné pour moi est le suivant:

  1. Dans le fichier de mappage Myclass.hbm.xml, définissez cascade="merge"
  2. SaveOrUpdate l'objet enfant / dépendant avant de l'affecter à l'objet parent.
  3. SaveOrUpdate l'objet parent.

Cependant, cette solution a des limites. c'est-à-dire que vous devez prendre soin de sauvegarder votre enfant / objet dépendant au lieu de laisser hiberner faire cela pour vous.

Si quelqu'un a une meilleure solution, j'aimerais voir.

Quoc Truong
la source
-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
la source
2
Vous devriez envisager de modifier votre réponse pour afficher le code qui est effectué, puis peut-être envisager le vidage complet du code à la fin. Dans l'état actuel des choses, nous devons faire défiler vers le bas et passer au hasard dans les commentaires. Voir Comment répondre .
Bugs