From 5cb50132f757cd4869a28eace6863fd6c948baa9 Mon Sep 17 00:00:00 2001
From: Mansoor Ali <muhammadmansoorali@outlook.com>
Date: Tue, 5 Oct 2021 07:00:20 +0500
Subject: [PATCH] BAEL-5118 (#11238)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* BAEL-5118 How to solve Hibernate “object references an unsaved transient instance” error

* BAEL-5118 How to solve Hibernate “object references an unsaved transient instance” error - refactoring
---
 .../transientobject/HibernateUtil.java        | 51 +++++++++++
 .../transientobject/entity/Address.java       | 74 ++++++++++++++++
 .../transientobject/entity/Author.java        | 71 ++++++++++++++++
 .../transientobject/entity/Book.java          | 72 ++++++++++++++++
 .../transientobject/entity/Department.java    | 70 ++++++++++++++++
 .../transientobject/entity/Employee.java      | 66 +++++++++++++++
 .../transientobject/entity/User.java          | 76 +++++++++++++++++
 .../HibernateTransientObjectUnitTest.java     | 84 +++++++++++++++++++
 8 files changed, 564 insertions(+)
 create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/HibernateUtil.java
 create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Address.java
 create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Author.java
 create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Book.java
 create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Department.java
 create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Employee.java
 create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/User.java
 create mode 100644 persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/transientobject/HibernateTransientObjectUnitTest.java

diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/HibernateUtil.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/HibernateUtil.java
new file mode 100644
index 0000000000..a40279661f
--- /dev/null
+++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/HibernateUtil.java
@@ -0,0 +1,51 @@
+package com.baeldung.hibernate.exception.transientobject;
+
+import java.util.Properties;
+
+import com.baeldung.hibernate.exception.transientobject.entity.Address;
+import com.baeldung.hibernate.exception.transientobject.entity.Department;
+import com.baeldung.hibernate.exception.transientobject.entity.Author;
+import com.baeldung.hibernate.exception.transientobject.entity.Book;
+import com.baeldung.hibernate.exception.transientobject.entity.Employee;
+import com.baeldung.hibernate.exception.transientobject.entity.User;
+import org.hibernate.SessionFactory;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.service.ServiceRegistry;
+
+public class HibernateUtil {
+    private static SessionFactory sessionFactory;
+
+    public static SessionFactory getSessionFactory() {
+        if (sessionFactory == null) {
+            try {
+                Configuration configuration = new Configuration();
+                Properties settings = new Properties();
+                settings.put(Environment.DRIVER, "org.hsqldb.jdbcDriver");
+                settings.put(Environment.URL, "jdbc:hsqldb:mem:transient");
+                settings.put(Environment.USER, "sa");
+                settings.put(Environment.PASS, "");
+                settings.put(Environment.DIALECT, "org.hibernate.dialect.HSQLDialect");
+                settings.put(Environment.SHOW_SQL, "true");
+                settings.put(Environment.USE_SQL_COMMENTS, "true");
+                settings.put(Environment.HBM2DDL_AUTO, "update");
+                configuration.setProperties(settings);
+                configuration.addAnnotatedClass(User.class);
+                configuration.addAnnotatedClass(Address.class);
+                configuration.addAnnotatedClass(Department.class);
+                configuration.addAnnotatedClass(Employee.class);
+                configuration.addAnnotatedClass(Book.class);
+                configuration.addAnnotatedClass(Author.class);
+
+
+                ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
+                        .applySettings(configuration.getProperties()).build();
+                sessionFactory = configuration.buildSessionFactory(serviceRegistry);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return sessionFactory;
+    }
+}
\ No newline at end of file
diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Address.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Address.java
new file mode 100644
index 0000000000..e450fc3c18
--- /dev/null
+++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Address.java
@@ -0,0 +1,74 @@
+package com.baeldung.hibernate.exception.transientobject.entity;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "address")
+public class Address {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private Long id;
+
+    @Column(name = "city")
+    private String city;
+
+    @Column(name = "street")
+    private String street;
+
+    @OneToOne(mappedBy = "address")
+    private User user;
+
+    public Address() {
+    }
+
+    public Address(String city, String street) {
+        this.city = city;
+        this.street = street;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+
+    public String getStreet() {
+        return street;
+    }
+
+    public void setStreet(String street) {
+        this.street = street;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+}
\ No newline at end of file
diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Author.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Author.java
new file mode 100644
index 0000000000..f1a88daa36
--- /dev/null
+++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Author.java
@@ -0,0 +1,71 @@
+package com.baeldung.hibernate.exception.transientobject.entity;
+
+import org.hibernate.annotations.Cascade;
+import org.hibernate.annotations.CascadeType;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+@Table(name = "author")
+public class Author {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private int id;
+
+    @Column(name = "name")
+    private String name;
+
+    @ManyToMany
+    @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.PERSIST})
+    @JoinColumn(name = "book_id")
+    private Set<Book> books = new HashSet<>();
+
+    public void addBook(Book book) {
+        books.add(book);
+    }
+
+    // standard getters and setters
+
+    public Author() {
+    }
+
+    public Author(String name) {
+        this.name = name;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Set<Book> getBooks() {
+        return books;
+    }
+
+    public void setBooks(Set<Book> books) {
+        this.books = books;
+    }
+}
diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Book.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Book.java
new file mode 100644
index 0000000000..91728430ea
--- /dev/null
+++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Book.java
@@ -0,0 +1,72 @@
+package com.baeldung.hibernate.exception.transientobject.entity;
+
+
+import org.hibernate.annotations.Cascade;
+import org.hibernate.annotations.CascadeType;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+@Table(name = "book")
+public class Book {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private int id;
+
+    @Column(name = "title")
+    private String title;
+
+    @ManyToMany
+    @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.PERSIST})
+    @JoinColumn(name = "author_id")
+    private Set<Author> authors = new HashSet<>();
+
+    public void addAuthor(Author author) {
+        authors.add(author);
+    }
+
+    // standard getters and setters
+
+    public Book() {
+    }
+
+    public Book(String title) {
+        this.title = title;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public Set<Author> getAuthors() {
+        return authors;
+    }
+
+    public void setAuthors(Set<Author> authors) {
+        this.authors = authors;
+    }
+}
diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Department.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Department.java
new file mode 100644
index 0000000000..4b8fa69964
--- /dev/null
+++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Department.java
@@ -0,0 +1,70 @@
+package com.baeldung.hibernate.exception.transientobject.entity;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+@Table(name = "department")
+public class Department {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private int id;
+
+    @Column(name = "name")
+    private String name;
+
+    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
+    private Set<Employee> employees = new HashSet<>();
+
+    public void addEmployee(Employee employee) {
+        employees.add(employee);
+    }
+
+    // standard getters and setters
+
+    public Department() {
+    }
+
+    public Department(int id) {
+        this.id = id;
+    }
+
+    public Department(int id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Set<Employee> getEmployees() {
+        return employees;
+    }
+
+    public void setEmployees(Set<Employee> employees) {
+        this.employees = employees;
+    }
+}
diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Employee.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Employee.java
new file mode 100644
index 0000000000..56443cce70
--- /dev/null
+++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/Employee.java
@@ -0,0 +1,66 @@
+package com.baeldung.hibernate.exception.transientobject.entity;
+
+
+import org.hibernate.annotations.Cascade;
+import org.hibernate.annotations.CascadeType;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "employee")
+public class Employee {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private int id;
+
+    @Column(name = "name")
+    private String name;
+
+    @ManyToOne
+    @Cascade(CascadeType.SAVE_UPDATE)
+    @JoinColumn(name = "department_id")
+    private Department department;
+
+    // standard getters and setters
+
+    public Employee() {
+    }
+
+    public Employee(String name) {
+        this.name = name;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Department getDepartment() {
+        return department;
+    }
+
+    public void setDepartment(Department department) {
+        this.department = department;
+    }
+}
diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/User.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/User.java
new file mode 100644
index 0000000000..eff1a88b51
--- /dev/null
+++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/transientobject/entity/User.java
@@ -0,0 +1,76 @@
+package com.baeldung.hibernate.exception.transientobject.entity;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "user")
+public class User {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private int id;
+
+    @Column(name = "first_name")
+    private String firstName;
+
+    @Column(name = "last_name")
+    private String lastName;
+
+    @OneToOne(cascade = CascadeType.ALL)
+    @JoinColumn(name = "address_id", referencedColumnName = "id")
+    private Address address;
+
+    public User() {
+    }
+
+    public User(String firstName, String lastName) {
+        this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public Address getAddress() {
+        return address;
+    }
+
+    public void setAddress(Address address) {
+        this.address = address;
+    }
+}
diff --git a/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/transientobject/HibernateTransientObjectUnitTest.java b/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/transientobject/HibernateTransientObjectUnitTest.java
new file mode 100644
index 0000000000..3f6d0effa9
--- /dev/null
+++ b/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/transientobject/HibernateTransientObjectUnitTest.java
@@ -0,0 +1,84 @@
+package com.baeldung.hibernate.exception.transientobject;
+
+import com.baeldung.hibernate.exception.transientobject.entity.Address;
+import com.baeldung.hibernate.exception.transientobject.entity.Department;
+import com.baeldung.hibernate.exception.transientobject.entity.Author;
+import com.baeldung.hibernate.exception.transientobject.entity.Book;
+import com.baeldung.hibernate.exception.transientobject.entity.Employee;
+import com.baeldung.hibernate.exception.transientobject.entity.User;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class HibernateTransientObjectUnitTest {
+
+    private static SessionFactory sessionFactory;
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @BeforeClass
+    public static void init() {
+        sessionFactory = HibernateUtil.getSessionFactory();
+    }
+
+    @Test
+    public void whenSaveEntitiesWithOneToOneAssociation_thenSuccess() {
+        User user = new User("Bob", "Smith");
+        Address address = new Address("London", "221b Baker Street");
+        user.setAddress(address);
+        Session session = sessionFactory.openSession();
+        session.beginTransaction();
+        session.save(user);
+        session.getTransaction().commit();
+        session.close();
+    }
+
+    @Test
+    public void whenSaveEntitiesWithOneToManyAssociation_thenSuccess() {
+        Department department = new Department();
+        department.setName("IT Support");
+        Employee employee = new Employee("John Doe");
+        employee.setDepartment(department);
+        Session session = sessionFactory.openSession();
+        session.beginTransaction();
+        session.save(employee);
+        session.getTransaction().commit();
+        session.close();
+    }
+
+    @Test
+    public void whenSaveEntitiesWithManyToManyAssociation_thenSuccess_1() {
+        Book book = new Book("Design Patterns: Elements of Reusable Object-Oriented Software");
+        book.addAuthor(new Author("Erich Gamma"));
+        book.addAuthor(new Author("John Vlissides"));
+        book.addAuthor(new Author("Richard Helm"));
+        book.addAuthor(new Author("Ralph Johnson"));
+        Session session = sessionFactory.openSession();
+        session.beginTransaction();
+        session.save(book);
+        session.getTransaction().commit();
+        session.close();
+    }
+
+    @Test
+    public void whenSaveEntitiesWithManyToManyAssociation_thenSuccess_2() {
+        Author author = new Author("Erich Gamma");
+        author.addBook(new Book("Design Patterns: Elements of Reusable Object-Oriented Software"));
+        author.addBook(new Book("Introduction to Object Orient Design in C"));
+        Session session = sessionFactory.openSession();
+        session.beginTransaction();
+        session.save(author);
+        session.getTransaction().commit();
+        session.close();
+    }
+
+    @AfterClass
+    public static void cleanUp() {
+        sessionFactory.close();
+    }
+}