diff --git a/spring-hibernate4/README.md b/spring-hibernate4/README.md
index 4b48fe7064..1f18e1d0e8 100644
--- a/spring-hibernate4/README.md
+++ b/spring-hibernate4/README.md
@@ -9,6 +9,7 @@
- [Sorting with Hibernate](http://www.baeldung.com/hibernate-sort)
- [Auditing with JPA, Hibernate, and Spring Data JPA](http://www.baeldung.com/database-auditing-jpa)
- [Stored Procedures with Hibernate](http://www.baeldung.com/stored-procedures-with-hibernate-tutorial)
+- [Hibernate: save, persist, update, merge, saveOrUpdate](http://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate/)
### Quick Start
diff --git a/spring-hibernate4/pom.xml b/spring-hibernate4/pom.xml
index 3652674614..4f5cd0c290 100644
--- a/spring-hibernate4/pom.xml
+++ b/spring-hibernate4/pom.xml
@@ -149,6 +149,13 @@
test
+
+ org.hsqldb
+ hsqldb
+ 2.3.4
+ test
+
+
diff --git a/spring-hibernate4/src/main/java/com/baeldung/persistence/model/Person.java b/spring-hibernate4/src/main/java/com/baeldung/persistence/model/Person.java
new file mode 100644
index 0000000000..6a95a7acf5
--- /dev/null
+++ b/spring-hibernate4/src/main/java/com/baeldung/persistence/model/Person.java
@@ -0,0 +1,31 @@
+package com.baeldung.persistence.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity
+public class Person {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+
+ private String name;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java b/spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java
new file mode 100644
index 0000000000..aadaefbe44
--- /dev/null
+++ b/spring-hibernate4/src/test/java/com/baeldung/persistence/save/SaveMethodsTest.java
@@ -0,0 +1,272 @@
+package com.baeldung.persistence.save;
+
+
+import com.baeldung.persistence.model.Person;
+import org.hibernate.HibernateException;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.service.ServiceRegistry;
+import org.junit.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * Testing specific implementation details for different methods:
+ * persist, save, merge, update, saveOrUpdate.
+ */
+public class SaveMethodsTest {
+
+ private static SessionFactory sessionFactory;
+
+ private Session session;
+
+ @BeforeClass
+ public static void beforeTests() {
+ Configuration configuration = new Configuration()
+ .addAnnotatedClass(Person.class)
+ .setProperty("hibernate.dialect", HSQLDialect.class.getName())
+ .setProperty("hibernate.connection.driver_class", org.hsqldb.jdbcDriver.class.getName())
+ .setProperty("hibernate.connection.url", "jdbc:hsqldb:mem:test")
+ .setProperty("hibernate.connection.username", "sa")
+ .setProperty("hibernate.connection.password", "")
+ .setProperty("hibernate.hbm2ddl.auto", "update");
+ ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(
+ configuration.getProperties()).build();
+ sessionFactory = configuration.buildSessionFactory(serviceRegistry);
+ }
+
+ @Before
+ public void setUp() {
+ session = sessionFactory.openSession();
+ session.beginTransaction();
+ }
+
+
+ @Test
+ public void whenPersistTransient_thenSavedToDatabaseOnCommit() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.persist(person);
+
+ session.getTransaction().commit();
+ session.close();
+
+ session = sessionFactory.openSession();
+ session.beginTransaction();
+
+ assertNotNull(session.get(Person.class, person.getId()));
+
+ }
+
+ @Test
+ public void whenPersistPersistent_thenNothingHappens() {
+
+ Person person = new Person();
+ person.setName("John");
+
+ session.persist(person);
+ Long id1 = person.getId();
+
+ session.persist(person);
+ Long id2 = person.getId();
+
+ assertEquals(id1, id2);
+ }
+
+ @Test(expected = HibernateException.class)
+ public void whenPersistDetached_thenThrowsException() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.persist(person);
+ session.evict(person);
+
+ session.persist(person);
+
+ }
+
+ @Test
+ public void whenSaveTransient_thenIdGeneratedImmediately() {
+
+ Person person = new Person();
+ person.setName("John");
+
+ assertNull(person.getId());
+
+ Long id = (Long) session.save(person);
+
+ assertNotNull(id);
+
+ session.getTransaction().commit();
+ session.close();
+
+ assertEquals(id, person.getId());
+
+ session = sessionFactory.openSession();
+ session.beginTransaction();
+
+ assertNotNull(session.get(Person.class, person.getId()));
+
+ }
+
+ @Test
+ public void whenSavePersistent_thenNothingHappens() {
+
+ Person person = new Person();
+ person.setName("John");
+ Long id1 = (Long) session.save(person);
+ Long id2 = (Long) session.save(person);
+ assertEquals(id1, id2);
+
+ }
+
+ @Test
+ public void whenSaveDetached_thenNewInstancePersisted() {
+
+ Person person = new Person();
+ person.setName("John");
+ Long id1 = (Long) session.save(person);
+ session.evict(person);
+
+ Long id2 = (Long) session.save(person);
+ assertNotEquals(id1, id2);
+
+ }
+
+ @Test
+ public void whenMergeDetached_thenEntityUpdatedFromDatabase() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.save(person);
+ session.evict(person);
+
+ person.setName("Mary");
+ Person mergedPerson = (Person) session.merge(person);
+
+ assertNotSame(person, mergedPerson);
+ assertEquals("Mary", mergedPerson.getName());
+
+ }
+
+ @Test
+ public void whenMergeTransient_thenNewEntitySavedToDatabase() {
+
+ Person person = new Person();
+ person.setName("John");
+ Person mergedPerson = (Person) session.merge(person);
+
+ session.getTransaction().commit();
+ session.beginTransaction();
+
+ assertNull(person.getId());
+ assertNotNull(mergedPerson.getId());
+
+ }
+
+ @Test
+ public void whenMergePersistent_thenReturnsSameObject() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.save(person);
+
+ Person mergedPerson = (Person) session.merge(person);
+
+ assertSame(person, mergedPerson);
+
+ }
+
+ @Test
+ public void whenUpdateDetached_thenEntityUpdatedFromDatabase() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.save(person);
+ session.evict(person);
+
+ person.setName("Mary");
+ session.update(person);
+ assertEquals("Mary", person.getName());
+
+ }
+
+ @Test(expected = HibernateException.class)
+ public void whenUpdateTransient_thenThrowsException() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.update(person);
+
+ }
+
+ @Test
+ public void whenUpdatePersistent_thenNothingHappens() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.save(person);
+
+ session.update(person);
+
+ }
+
+ @Test
+ public void whenSaveOrUpdateDetached_thenEntityUpdatedFromDatabase() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.save(person);
+ session.evict(person);
+
+ person.setName("Mary");
+ session.saveOrUpdate(person);
+ assertEquals("Mary", person.getName());
+
+ }
+
+ @Test
+ public void whenSaveOrUpdateTransient_thenSavedToDatabaseOnCommit() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.saveOrUpdate(person);
+
+ session.getTransaction().commit();
+ session.close();
+
+ session = sessionFactory.openSession();
+ session.beginTransaction();
+
+ assertNotNull(session.get(Person.class, person.getId()));
+
+
+ }
+
+ @Test
+ public void whenSaveOrUpdatePersistent_thenNothingHappens() {
+
+ Person person = new Person();
+ person.setName("John");
+ session.save(person);
+
+ session.saveOrUpdate(person);
+
+ }
+
+ @After
+ public void tearDown() {
+ session.getTransaction().commit();
+ session.close();
+ }
+
+ @AfterClass
+ public static void afterTests() {
+ sessionFactory.close();
+ }
+
+}