【问题标题】:How save, saveUpdate and merge methods works in hibernate?保存、保存更新和合并方法在休眠中如何工作?
【发布时间】:2015-05-13 09:06:21
【问题描述】:

我对保存、saveOrUpdate 和合并有点困惑。以下是我曾经检查过的测试,现在变得更加困惑:)。

DESC:我有 EmpCerts 实体。 Emp 和 Certs 具有使用一对多映射的父子关系。我从 Main 方法调用 saveEmployee() 方法并调用 save()saveOrUpdate()merge() 方法一一测试其行为

Emp 实体

@Entity
@Table(name = "tmp_emp")
public class Emp {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "EMP_ID")
private Long empId; 

@Column(name = "FIRST_NAME")
String firstName;

@Column(name = "LAST_NAME")
String lastName;

@Column(name= "SALARY")
Integer salary;

@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "emp")
Set<Certs> certSet;

//  getters-setters

证书实体

@Entity
@Table(name = "tmp_certs")
public class Certs {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "EMPCERT_ID")
private Long empCertId; 

@Column(name = "CERT_NAME")
String certName;

@ManyToOne(targetEntity = Emp.class)
@JoinColumn(name = "EMP_ID")
private Emp emp;

//getters-setters

主要方法

public static void main(String[] args) {
    AnnotationConfiguration config = new AnnotationConfiguration();     
    config.configure("hibernate-config.xml").addAnnotatedClass(Emp.class);
    config.configure("hibernate-config.xml").addAnnotatedClass(Certs.class);
    SessionFactory factory = config.buildSessionFactory();

// Creating 1st Cert object, set cert name
    Certs cert1 = new Certs();
    cert1.setCertName("OCJP");

// Creating 2nd Cert object, set cert name
    Certs cert2 = new Certs();
    cert2.setCertName("PMP");

// Creating a Hashset to store Certs
    Set<Certs> certsSet = new HashSet<Certs>();

// Adding certs to Hashset
    certsSet.add(cert1);
    certsSet.add(cert2);

// Creation of Emp Object.
    Emp emp = new Emp();

// First call to saveEmployee method.
    saveEmployee(emp, "Rick", "Ved", 3000, factory, certsSet);

// Second call to saveEmployee method with same emp and certs hashset.
    saveEmployee(emp, "George", "Tank", 5000, factory, certsSet);

}

下面是 saveEmployee 方法。我已经一一使用了saveOrUpdate()save()merge()方法。

private static void saveEmployee(Emp emp,String fname,String lname,Integer sal, SessionFactory factory, Set<Certs> certsSet) {      
    Session session = factory.openSession();
    Transaction tx =null;
    try{
        tx = session.beginTransaction();

// Setting Firstname, Lastname and Salary of emp object.            
        emp.setFirstName(fname);
        emp.setLastName(lname);
        emp.setSalary(sal);

// Iterate Cert Hashset and set empId to Cert Objects.
        Iterator<Certs> itr = certsSet.iterator();
        while(itr.hasNext()){               
            Certs c = itr.next(); c.setEmp(emp);
        }
        emp.setCertSet(certsSet);
        session.saveOrUpdate(emp);  // CASE 1
//      session.save(emp); // CASE 2
//      session.merge(emp);  //CASE 3
        session.flush();
        tx.commit();
    }catch(HibernateException e){
        if (tx!=null) tx.rollback();
         e.printStackTrace(); 
      }finally {
// Close the session.
         session.close(); 
      }
}

案例一:saveOrUpdate()

方法 save() 和 merge() 已注释。结果如下:

tmp_emp 表:

tmp_certs

因此,从这个和休眠 sqls 中,我可以推断出 Emp 实体和 Certs 是首先插入的,然后所有 3 个都被更新。我想它可以通过初始插入然后更新。

案例2:save()

方法 saveOrUpdate() 和 merge() 已注释。结果如下:

tmp_emp

tmp_certs

这里调用了 save 方法。两个会话再次使用相同的 Emp 和 Certs 对象。这次 emp 对象被视为 2 个对象,两个员工都保存到 emp 表中。但是证书再次被视为相同的对象。他们被插入,后来被更新(使用新的 empId)。 Hibernate SQL 也显示相同。 2 次插入 Emp,2 次插入证书,然后 2 次更新相同的证书(可以看到 empId: 112 在证书上更新)

案例 3:merge()

方法save()和saveOrUpdate()注释。结果如下

tmp_emp

tmp_certs

这里每次将 Emp 和 Certs 对象都视为新对象时使用 merge()。所以在 Hibernate sql 中有 2 个用于 Emp 的插入和 4 个用于 Certs 的插入。

我经历了这个LINK,但无法与这些案例相关联。对于理解这种行为的任何帮助将不胜感激。

【问题讨论】:

    标签: java hibernate


    【解决方案1】:

    我没有读到你的问题,你想要做什么有点令人困惑。但是,我会在这里解释一下。

    1. session.save() 上的 transient/detached 对象将始终在数据库表中创建一行。
    2. session.saveOrUpdate() 将保存 transient 对象 or 更新 detached 对象。 如果会话中已经存在持久性对象,那么它将抛出一个异常,说明具有该 ID 的对象已经在会话中。

    例如:

    public void saveSomething(Something one){//here one.id = 33 too
        ..
        Something second = (Something) session.get(Something.class, 33); //get entity of id 33
    
        //try to update one here
        session.saveOrUpdate(one); 
    
        //commit your transaction...  EXCEPTION WILL BE THROWN AS SOMETHING OBJECT WITH 33 ID IS IN THE SESSION ALREADY
    }
    
    1. session.merge() 会将会话中的值替换为分离对象的值。它不关心你对会话中的实际实体做了什么。不会抛出异常。

    例如:

    public void saveSomething(Something one){
        //here one.id = 33 too
        //one.name ='Abdulla'
    
        ..
        Something second = (Something) session.get(Something.class, 33); //get entity of id 33. 
        //second.name = 'Frank'
        //second.age = 33
        //change name here to Mark
        second.setName('Mark');
        //try to merge one here into the session.
        session.merge(one);     
        //now the session will have values of 'one' in it as they are replaced
        Something third = (Something) session.get(Something.class, 33);
        //now you'll get the following values if you try to get the 
        //value as you have overwritten what is already present in the session. 
        //You'll not get Mark as they are replaced by the merge operation.
        //third.id = 33
        //third.age = 33    
        //third.name ='Abdulla' 
    }
    

    【讨论】:

    • 宙斯,实际上我有一个 emp 实体和 2 个 certs 实体。 emp 和 certs 具有使用一对多映射的父子关系。我从 main 方法调用 saveEmployee 方法并调用 savesaveOrUpdatemerge 测试其行为的方法。在第一次调用 saveEmployee 对象后,第二次调用时分离。 saveOrUpdate() 根据您的解释是非常正确的。但是 save() 已在 emp 中插入记录,并针对证书进行了更新。 merge() 是对 saveEmployee 的两次调用都进行了插入的那个。如果不够清楚,请告诉我。
    • 有人能解释一下到底发生了什么吗?我尽力解释了这些场景,如果我的问题不清楚,请告知。
    • 嘿..我有一段时间没有关注堆栈溢出了。获得关注的最佳方法是编辑您的问题。添加更多详细信息。
    猜你喜欢
    • 2011-07-15
    • 2016-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-16
    • 2012-01-18
    • 2011-09-20
    • 2012-08-09
    相关资源
    最近更新 更多