Hibernate Difference between update() and merge()

In order to explain the difference between update and merge, I have created an example, which you can execute or just read it to understand the difference.

1. First Create a database medireports: (DB=MySQL)

     
CREATE DATABASE `medireports` 
    

2. Now Create a 'reports' table:


CREATE TABLE `reports` (
  `reportid` varchar(20) NOT NULL,
  `report` mediumblob,
  `phonenumber` bigint(20) NOT NULL,
  `labid` varchar(45) DEFAULT NULL,
  `retainreport` varchar(45) DEFAULT NULL,
  `reportdate` date DEFAULT NULL,
  `filename` varchar(65) DEFAULT NULL,
  PRIMARY KEY (`reportid`)
) 

3. Insert data into the reports table:


INSERT INTO `medireports`.`reports`(`reportid`,`phonenumber`,`retainreport`,`reportdate`,`filename`)
VALUES ('ZL1234', '9999666222', '0', '2014-03-13', 'jqModal.js'); INSERT INTO `medireports`.`reports`(`reportid`,`phonenumber`,`retainreport`,`reportdate`,`filename`)
VALUES ('ZL123456', '9999666444', '0', '2014-03-13', 'jquery.js');

4. Now create a pojo 'ReportMap' class mapped to 'reports' table in the database:


import java.io.Serializable;
import java.sql.Blob;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;

@Entity
@Table(name = "reports")
public class ReportMap implements Serializable {

 private static final long serialVersionUID = 6381346427683479982L;

 @Column(name = "labid")
 private String labId;

 @Column(name = "phonenumber")
 private String phoneNumber;

 @Lob
 @Column(name = "report")
 private Blob report;

 @Column(name = "reportdate")
 private Date reportDate;

 @Id
 @Column(name = "reportid")
 private String reportId;

 @Column(name = "retainreport")
 private boolean retainReport;

 @Column(name = "filename")
 private String fileName;

 public String getLabId() {
  return labId;
 }

 public void setLabId(String labId) {
  this.labId = labId;
 }

 public String getPhoneNumber() {
  return phoneNumber;
 }

 public void setPhoneNumber(String phoneNumber) {
  this.phoneNumber = phoneNumber;
 }

 public Blob getReport() {
  return report;
 }

 public void setReport(Blob report) {
  this.report = report;
 }

 public static long getSerialversionuid() {
  return serialVersionUID;
 }

 public Date getReportDate() {
  return reportDate;
 }

 public void setReportDate(Date reportDate) {
  this.reportDate = reportDate;
 }

 public String getReportId() {
  return reportId;
 }

 public void setReportId(String reportId) {
  this.reportId = reportId;
 }

 public boolean isRetainReport() {
  return retainReport;
 }

 public void setRetainReport(boolean retainReport) {
  this.retainReport = retainReport;
 }

 public String getFileName() {
  return fileName;
 }

 public void setFileName(String fileName) {
  this.fileName = fileName;
 }

 @Override
 public String toString() {
  return "ReportMap [labName=" + labId + ", phoneNumber=" + phoneNumber
    + ", report=" + report + ", reportDate=" + reportDate
    + ", reportId=" + reportId + ", retainReport=" + retainReport
    + ", fileName=" + fileName + "]";
 }

}

Now we will analyze the various scenarios where merge() and update() works and also where update does not work.

5. Example of update()working in persistent state:

To understand it in a better way, have a look at the following screenshot of the reports table before update():
Now lets fetch the report having "reportid=ZL1234", modify it and update it in the same session.
  1. Session session1 = LoadCofinguration.getSessionFactory().openSession();
  2. ReportMap reportMap1 = new ReportMap();
  3. try {
  4. Transaction transaction = session1.beginTransaction();
  5. reportMap1 = (ReportMap)session1.get(ReportMap.class, "ZL1234");
  6. //Modify the reportMap1 object.
  7. reportMap1.setRetainReport(true);
  8. //update the reportMap1 object.
  9. session1.update(reportMap1);
  10. transaction.commit();
  11. } catch (HibernateException hbe) {
  12. hbe.printStackTrace();
  13. } finally {
  14. session1.close();
  15. }
Let's have a closer look at the example:
  1. On line number 1 we are opening a session.
  2. On line number 5 we are loading a record into reportMap1 object.
  3. On line number 7 we are modifying the object by setting retainReport property to true.
  4. And in the line number 9 we are updating the reportMap1 object in the same session.
Following is the screenshot of the reports table after update:
Hence proved that update() works in the persistent sate.

6. Example of update() not working in detached state. And merge() working in detached state:

Now lets modify the phone number of the reportMap1 instance after the session1 is closed and try to update it in a new session.
  1. Session session1 = LoadCofinguration.getSessionFactory().openSession();
  2. ReportMap reportMap1 = new ReportMap();
  3. try {
  4. Transaction transaction = session1.beginTransaction();
  5. reportMap1 = (ReportMap)session1.get(ReportMap.class, "ZL1234");
  6. //Modify the reportMap1 object.
  7. reportMap1.setRetainReport(true);
  8. //update the reportMap1 object.
  9. session1.update(reportMap1);
  10. transaction.commit();
  11. } catch (HibernateException hbe) {
  12. hbe.printStackTrace();
  13. } finally {
  14. session1.close();
  15. }
  16. // Modify the phone number for reportMap1 object.
  17. reportMap1.setPhoneNumber("9999666333");
  18. Session session2 = LoadCofinguration.getSessionFactory().openSession();
  19. try {
  20. Transaction transaction = session2.beginTransaction();
  21. ReportMap reportMap2 = (ReportMap)session2.get(ReportMap.class, "ZL1234");
  22. // The update will fail as both reportMap1 and reportMap2 are referring to the same record in DB.
  23. session2.update(reportMap1);
  24. //session2.merge(reportMap1);
  25. transaction.commit();
  26. } catch (HibernateException hbe) {
  27. hbe.printStackTrace();
  28. } finally {
  29. session2.close();
Let's again analyze the code line by line:
  1. After closing the session1 on line number 14, we have updated the reportMap1 instance with phoneNumber at line number 17.
  2. Now at line number 18 we are creating a new session (session2) and again loading the same object from the database into reportMap2 object.
  3. At line number 23 we are calling update() method to update the modified reportMap1 instance.
  4. The update will fail here and will thorough org.hibernate.NoUniqueObjectException as both the objects (reportMap1 and reportMap2) are referring to the same record in the database. Technically saying both are having the same identifier value.
  5. Following is the screenshot for your reference, which displays the exception thrown:
  6. Now if you comment line number 23 and un-comment line number 24 to call the merge() method, the detached instance reportMap1 will be merged into reportMap2 and the record in the database will be updated.
Following is the screenshot after calling merge() on session2, where in the record is modified with new phoneNumber value:
Hence proved that update() does not work when existing object in the new session and detached object have same identifier. However, merge() works for this scenario.

7. Example of update() working in detached state:

Now again modify the reportMap1 object and update it in the new session. This time we will update the labId property for reportMap1 and update it. Following is the code for your reference:
  1. Session session1 = LoadCofinguration.getSessionFactory().openSession();
  2. ReportMap reportMap1 = new ReportMap();
  3. try {
  4. Transaction transaction = session1.beginTransaction();
  5. reportMap1 = (ReportMap)session1.get(ReportMap.class, "ZL1234");
  6. //Modify the reportMap1 object.
  7. reportMap1.setRetainReport(true);
  8. //update the reportMap1 object.
  9. session1.update(reportMap1);
  10. transaction.commit();
  11. } catch (HibernateException hbe) {
  12. hbe.printStackTrace();
  13. } finally {
  14. session1.close();
  15. }
  16. // Modify the phone number for reportMap1 object.
  17. reportMap1.setPhoneNumber("9999666333");
  18. Session session2 = LoadCofinguration.getSessionFactory().openSession();
  19. try {
  20. Transaction transaction = session2.beginTransaction();
  21. ReportMap reportMap2 = (ReportMap)session2.get(ReportMap.class, "ZL1234");
  22. //session2.update(reportMap1);
  23. session2.merge(reportMap1);
  24. transaction.commit();
  25. } catch (HibernateException hbe) {
  26. hbe.printStackTrace();
  27. } finally {
  28. session2.close();
  29. }
  30. reportMap1.setLabId("ZuluLabs");
  31. Session session3 = LoadCofinguration.getSessionFactory().openSession();
  32. try {
  33. Transaction transaction = session3.beginTransaction();
  34. // Load ReportMap with a new identified "ZL123456".
  35. ReportMap reportMap3 = (ReportMap)session3.get(ReportMap.class, "ZL123456");
  36. session3.update(reportMap1);
  37. transaction.commit();
  38. } catch (HibernateException hbe) {
  39. hbe.printStackTrace();
  40. } finally {
  41. session3.close();
  42. }
Now closely look at code modifying reportMap1 instance for labId property and updating the reportMap1 instance in a new session:
  1. On line number 33, reportMap1 instance is again modified to set the labId property to "ZuluLabs".
  2. On line number 34 we are opening a new session (session3).
  3. Now on line number 38 we loading a new ReportMap object into reportMap3 with ZL123456 as the identifier value .
  4. In the next line we are calling update() method for reportMap1 object.
  5. This time the update will succeed as the identifiers for reportMap1 (ZL1234) and reportMap3 (ZL123456) are different.
Following is screenshot of the reports table after calling update():
Hence proved that update() also works for detached objects, however, if the identifier value is same as the loaded instance in the new session, then it will through and exception, as stated in the exception explained above.

Comments

Popular posts from this blog

Spring Security - Adding more information to the authenticated user.

Running tomcat maven plugin in Debug mode (in STS, Eclipse and MyEclipse)