合并和会话之间的区别是什么?

我注意到有时对于父对象/子对象或多对多关系,我需要调用 SaveOrUpdateMerge。通常,当我需要调用 SaveOrUpdate时,我在调用 Merge时遇到的异常与没有首先保存瞬态对象有关。

请解释一下这两者之间的区别。

93845 次浏览

This is from section 10.7. Automatic state detection of the Hibernate Reference Documentation:

saveOrUpdate() does the following:

  • if the object is already persistent in this session, do nothing
  • if another object associated with the session has the same identifier, throw an exception
  • if the object has no identifier property, save() it
  • if the object's identifier has the value assigned to a newly instantiated object, save() it
  • if the object is versioned (by a <version> or <timestamp>), and the version property value is the same value assigned to a newly instantiated object, save() it
  • otherwise update() the object

and merge() is very different:

  • if there is a persistent instance with the same identifier currently associated with the session, copy the state of the given object onto the persistent instance
  • if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance
  • the persistent instance is returned
  • the given instance does not become associated with the session, it remains detached

You should use Merge() if you are trying to update objects that were at one point detached from the session, especially if there might be persistent instances of those objects currently associated with the session. Otherwise, using SaveOrUpdate() in that case would result in an exception.

As I understand it, merge() will take an object that may not be associated with the current session, and copy its state (property values, etc.) to an object that is associated with the current session (with the same PK value/identifier, of course).

saveOrUpdate() will call Save or Update on your session, based on a given object's identity value.

SaveOrUpdateCopy() is now deprecated as of NHibernate 3.1. Merge() should be used instead.

I found this link that did a pretty good job explaining this type of exception:

What worked for me is the following:

  1. In the mapping Myclass.hbm.xml file, set cascade="merge"
  2. SaveOrUpdate the child/dependent object first before assigning it to the parent object.
  3. SaveOrUpdate the parent object.

However, this solution has limitations. i.e., you have to take care of saving your child/dependent object instead of letting hibernate doing that for you.

If anyone has a better solution, I'd like to see.

** Update()**

:- if you are sure that the session does not contains an already persistent instance with the same identifier then use update to save the data in hibernate

** Merge()**

:-if you want to save your modifications at any time with out knowing about the state of an session then use merge() in hibernate.

@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();
}


}
}