ความแตกต่างระหว่าง session.Merge และ session.SaveOrUpdate คืออะไร?


87

ผมสังเกตเห็นบางครั้งก็มีผู้ปกครอง / วัตถุลูกของฉันหรือความสัมพันธ์ที่หลายต่อหลายคนที่ฉันต้องการที่จะเรียกทั้งสองหรือSaveOrUpdate Mergeโดยปกติแล้วเมื่อฉันต้องการโทรSaveOrUpdateข้อยกเว้นที่ฉันได้รับMergeจะเกี่ยวข้องกับวัตถุชั่วคราวที่ไม่ได้รับการบันทึกก่อน

โปรดอธิบายความแตกต่างระหว่างทั้งสอง

คำตอบ:


158

มาจากส่วนที่10.7 การตรวจจับสถานะอัตโนมัติของเอกสารอ้างอิงไฮเบอร์เนต:

saveOrUpdate () ทำสิ่งต่อไปนี้:

  • หากวัตถุนั้นคงอยู่ในเซสชันนี้อยู่แล้วไม่ต้องทำอะไรเลย
  • หากออบเจ็กต์อื่นที่เชื่อมโยงกับเซสชันมีตัวระบุเดียวกันให้โยนข้อยกเว้น
  • ถ้าวัตถุไม่มีคุณสมบัติตัวระบุบันทึก ()
  • หากตัวระบุของอ็อบเจ็กต์มีค่าที่กำหนดให้กับอ็อบเจ็กต์ที่สร้างอินสแตนซ์ใหม่ให้บันทึก ()
  • หากอ็อบเจ็กต์เป็นเวอร์ชัน (โดย <version> หรือ <timestamp>) และค่าคุณสมบัติเวอร์ชันเป็นค่าเดียวกันกับที่กำหนดให้กับอ็อบเจ็กต์ที่สร้างอินสแตนซ์ใหม่ให้บันทึก ()
  • มิฉะนั้นอัปเดต () วัตถุ

และ merge () แตกต่างกันมาก:

  • หากมีอินสแตนซ์ถาวรที่มีตัวระบุเดียวกันที่เชื่อมโยงกับเซสชันอยู่ในขณะนี้ให้คัดลอกสถานะของออบเจ็กต์ที่กำหนดไปยังอินสแตนซ์ถาวร
  • หากไม่มีอินสแตนซ์ถาวรที่เชื่อมโยงกับเซสชันในขณะนี้ให้ลองโหลดจากฐานข้อมูลหรือสร้างอินสแตนซ์ถาวรใหม่
  • อินสแตนซ์ถาวรจะถูกส่งคืน
  • อินสแตนซ์ที่ระบุไม่เชื่อมโยงกับเซสชัน แต่จะยังคงแยกออกจากกัน

คุณควรใช้ Merge () หากคุณกำลังพยายามอัปเดตอ็อบเจ็กต์ที่อยู่ในจุดหนึ่งที่แยกออกจากเซสชันโดยเฉพาะอย่างยิ่งหากอาจมีอินสแตนซ์ถาวรของอ็อบเจ็กต์เหล่านั้นที่เชื่อมโยงกับเซสชัน มิฉะนั้นการใช้ SaveOrUpdate () ในกรณีนั้นจะทำให้เกิดข้อยกเว้น


คำตอบที่ดี ... ฉันสงสัย - ถ้าฉันใช้ merge ในเอนทิตีใหม่จะมีเหตุผลใดที่จะใช้ save afterwords หรือฉันสามารถสมมติว่า merge ได้สร้างเอนทิตีใหม่ใน DB อย่างแน่นอน? (และถ้าเป็นเอนทิตีแยกเมื่อผสานการเปลี่ยนแปลงจะถูกละเว้นไปยังฐานข้อมูลโดยอัตโนมัติหรือไม่)
Dani

5
คุณแน่ใจเกี่ยวกับเรื่องนี้หรือไม่? การดูที่แหล่ง NHiberante SaveOrUpdateCopy จะทริกเกอร์เหตุการณ์ Merge ที่มีพารามิเตอร์เดียวกันกับฟังก์ชัน Merge ฉันคิดว่ามันเหมือนกันฟังก์ชัน SaveOrUpdateCopy เป็นสิ่งที่มีอยู่ใน hibernate / nhibernate ตั้งแต่ 1.0 ฟังก์ชัน Merge เป็นฟังก์ชันใหม่และถูกเพิ่มเข้าสู่โหมดไฮเบอร์เนตเพื่อให้สอดคล้องกับมาตรฐานจาวาใหม่ (ฉันคิดว่า)
Torkel

5
@Torkel - SaveOrUpdateCopyไม่เหมือนกับSaveOrUpdate. ฉันไม่แน่ใจว่าผู้ถามต้องการเปรียบเทียบMergeอดีตหรืออย่างหลัง SaveOrUpdateCopyเป็นวิธีการที่ล้าสมัยในปัจจุบันซึ่งรวมเข้ากับ NHibernate ก่อนที่จะMergeนำเข้า
codekaizen

สิ่งที่ควรรู้ ... SaveOrUpdate ยังคงถูกใช้อย่างมากในบทช่วยสอน
anael

9

ขณะที่ผมเข้าใจว่ามันmerge()จะใช้วัตถุที่ไม่อาจจะเกี่ยวข้องกับการใช้งานปัจจุบันและคัดลอกรัฐ (ค่าทรัพย์สิน ฯลฯ ) ไปยังวัตถุที่ถูกเชื่อมโยงกับการใช้งานปัจจุบัน (กับ PK เดียวกันค่า / ตัวบ่งชี้ของ แน่นอน).

saveOrUpdate()จะเรียกบันทึกหรืออัปเดตในเซสชันของคุณตามค่าเอกลักษณ์ของวัตถุที่กำหนด


4

SaveOrUpdateCopy()ตอนนี้เลิกใช้แล้วตั้งแต่ NHibernate 3.1 Merge()ควรใช้แทน


9
มันเป็นเรื่องSaveOrUpdateCopyที่มีการทำเครื่องหมายไม่Obsolete SaveOrUpdateดูเหมือนจะมีความสับสนระหว่างสองวิธีที่แตกต่างกันในคำถามนี้และคำตอบที่ตามมา
codekaizen

2
** Update()**

: - ถ้าคุณแน่ใจว่าเซสชันนั้นไม่มีอินสแตนซ์ถาวรที่มีตัวระบุเดียวกันอยู่แล้วให้ใช้การอัปเดตเพื่อบันทึกข้อมูลในโหมดไฮเบอร์เนต

** Merge()**

: - หากคุณต้องการบันทึกการแก้ไขของคุณได้ตลอดเวลาโดยไม่ทราบเกี่ยวกับสถานะของเซสชันจากนั้นใช้ merge () ในโหมดไฮเบอร์เนต


1

ฉันพบลิงค์นี้ซึ่งทำได้ดีทีเดียวที่อธิบายข้อยกเว้นประเภทนี้:

สิ่งที่ใช้ได้ผลสำหรับฉันมีดังต่อไปนี้:

  1. ในการแมปไฟล์ Myclass.hbm.xml ให้ตั้งค่า cascade="merge"
  2. SaveOrUpdate ลูก / วัตถุขึ้นอยู่กับก่อนกำหนดให้กับวัตถุแม่
  3. SaveOrUpdate วัตถุหลัก

อย่างไรก็ตามการแก้ปัญหานี้มีข้อ จำกัด กล่าวคือคุณต้องดูแลรักษาบุตรหลานของคุณ / วัตถุที่ต้องพึ่งพาแทนที่จะปล่อยให้จำศีลทำเพื่อคุณ

ถ้าใครมีวิธีแก้ปัญหาที่ดีกว่านี้ผมอยากเห็น


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

    }
}

2
คุณควรพิจารณาแก้ไขคำตอบของคุณเพื่อแสดงรหัสที่ได้รับผลจากนั้นอาจพิจารณาการถ่ายโอนข้อมูลแบบเต็มในตอนท้าย ในฐานะที่เป็นเราต้องเลื่อนลงและดูความคิดเห็น ดูวิธีการตอบ
Bugs
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.