diff --git a/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/CreditCardPayment.java b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/CreditCardPayment.java
new file mode 100644
index 0000000000..6eb41f7ccc
--- /dev/null
+++ b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/CreditCardPayment.java
@@ -0,0 +1,20 @@
+package com.baeldung.jpa.hibernateunproxy;
+
+import javax.persistence.Entity;
+import java.math.BigDecimal;
+
+@Entity
+public class CreditCardPayment extends Payment {
+
+ private String cardNumber;
+
+ CreditCardPayment(BigDecimal amount, WebUser webUser, String cardNumber) {
+ this.amount = amount;
+ this.webUser = webUser;
+ this.cardNumber = cardNumber;
+ }
+
+ protected CreditCardPayment() {
+ }
+
+}
diff --git a/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/Payment.java b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/Payment.java
new file mode 100644
index 0000000000..9e70da5f65
--- /dev/null
+++ b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/Payment.java
@@ -0,0 +1,47 @@
+package com.baeldung.jpa.hibernateunproxy;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.ManyToOne;
+import java.math.BigDecimal;
+import java.util.Objects;
+
+@Entity
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public abstract class Payment {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ protected WebUser webUser;
+
+ protected BigDecimal amount;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ Payment payment = (Payment) o;
+
+ return Objects.equals(id, payment.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ protected Payment() {
+ }
+
+}
diff --git a/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/PaymentReceipt.java b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/PaymentReceipt.java
new file mode 100644
index 0000000000..530839eef4
--- /dev/null
+++ b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/PaymentReceipt.java
@@ -0,0 +1,53 @@
+package com.baeldung.jpa.hibernateunproxy;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+import java.util.Objects;
+import java.util.UUID;
+
+@Entity
+public class PaymentReceipt {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ @OneToOne(fetch = FetchType.LAZY)
+ private Payment payment;
+
+ private String transactionNumber;
+
+ PaymentReceipt(Payment payment) {
+ this.payment = payment;
+ this.transactionNumber = UUID.randomUUID().toString();
+ }
+
+ public Payment getPayment() {
+ return payment;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ PaymentReceipt that = (PaymentReceipt) o;
+
+ return Objects.equals(id, that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ protected PaymentReceipt() {
+ }
+
+}
diff --git a/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/WebUser.java b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/WebUser.java
new file mode 100644
index 0000000000..d3f82bacd4
--- /dev/null
+++ b/persistence-modules/java-jpa-3/src/main/java/com/baeldung/jpa/hibernateunproxy/WebUser.java
@@ -0,0 +1,42 @@
+package com.baeldung.jpa.hibernateunproxy;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import java.util.Objects;
+
+@Entity
+public class WebUser {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ private String name;
+
+ WebUser(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ WebUser webUser = (WebUser) o;
+
+ return Objects.equals(id, webUser.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ protected WebUser() {
+ }
+
+}
diff --git a/persistence-modules/java-jpa-3/src/main/resources/META-INF/persistence.xml b/persistence-modules/java-jpa-3/src/main/resources/META-INF/persistence.xml
index f428fea07b..19ecae8491 100644
--- a/persistence-modules/java-jpa-3/src/main/resources/META-INF/persistence.xml
+++ b/persistence-modules/java-jpa-3/src/main/resources/META-INF/persistence.xml
@@ -77,4 +77,24 @@
+
+ org.hibernate.jpa.HibernatePersistenceProvider
+ com.baeldung.jpa.hibernateunproxy.Payment
+ com.baeldung.jpa.hibernateunproxy.CreditCardPayment
+ com.baeldung.jpa.hibernateunproxy.PaymentReceipt
+ com.baeldung.jpa.hibernateunproxy.WebUser
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/persistence-modules/java-jpa-3/src/test/java/com/baeldung/jpa/hibernateunproxy/HibernateProxyIntegrationTest.java b/persistence-modules/java-jpa-3/src/test/java/com/baeldung/jpa/hibernateunproxy/HibernateProxyIntegrationTest.java
new file mode 100644
index 0000000000..a831639f5d
--- /dev/null
+++ b/persistence-modules/java-jpa-3/src/test/java/com/baeldung/jpa/hibernateunproxy/HibernateProxyIntegrationTest.java
@@ -0,0 +1,75 @@
+package com.baeldung.jpa.hibernateunproxy;
+
+import org.hibernate.Hibernate;
+import org.hibernate.proxy.HibernateProxy;
+import org.junit.Assert;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import java.math.BigDecimal;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class HibernateProxyIntegrationTest {
+
+ private static EntityManager entityManager;
+
+ @BeforeAll
+ public static void setup() {
+ EntityManagerFactory factory = Persistence.createEntityManagerFactory("jpa-h2-hibernate-unproxy");
+ entityManager = factory.createEntityManager();
+ populateH2DB();
+ }
+
+ @Test
+ public void givenPaymentReceipt_whenAccessingPayment_thenVerifyType() {
+ PaymentReceipt paymentReceipt = entityManager.find(PaymentReceipt.class, 3L);
+ Assert.assertEquals(true, paymentReceipt.getPayment() instanceof HibernateProxy);
+ }
+
+ @Test
+ public void givenWebUserProxy_whenCreatingPayment_thenExecuteSingleStatement() {
+ entityManager.getTransaction().begin();
+
+ WebUser webUser = entityManager.getReference(WebUser.class, 1L);
+ Payment payment = new CreditCardPayment(new BigDecimal(100), webUser, "CN-1234");
+ entityManager.persist(payment);
+
+ entityManager.getTransaction().commit();
+ Assert.assertEquals(true, webUser instanceof HibernateProxy);
+ }
+
+ @Test
+ public void givenPaymentReceipt_whenCastingPaymentToConcreteClass_thenThrowClassCastException() {
+ PaymentReceipt paymentReceipt = entityManager.find(PaymentReceipt.class, 3L);
+ assertThrows(ClassCastException.class, () -> {
+ CreditCardPayment creditCardPayment = (CreditCardPayment) paymentReceipt.getPayment();
+ });
+ }
+
+ @Test
+ public void givenPaymentReceipt_whenPaymentIsUnproxied_thenReturnRealEntityObject() {
+ PaymentReceipt paymentReceipt = entityManager.find(PaymentReceipt.class, 3L);
+ Assert.assertEquals(true, Hibernate.unproxy(paymentReceipt.getPayment()) instanceof CreditCardPayment);
+ }
+
+ private static void populateH2DB() {
+ entityManager.getTransaction().begin();
+
+ WebUser webUser = new WebUser("name");
+ entityManager.persist(webUser);
+
+ Payment payment = new CreditCardPayment(new BigDecimal(100), webUser, "CN-1234");
+ entityManager.persist(payment);
+
+ PaymentReceipt receipt = new PaymentReceipt(payment);
+ entityManager.persist(receipt);
+
+ entityManager.getTransaction().commit();
+ entityManager.clear();
+ }
+
+}