diff --git a/persistence-modules/spring-data-jpa-repo-3/pom.xml b/persistence-modules/spring-data-jpa-repo-3/pom.xml
index 8cd8ca7f61..d406d25c05 100644
--- a/persistence-modules/spring-data-jpa-repo-3/pom.xml
+++ b/persistence-modules/spring-data-jpa-repo-3/pom.xml
@@ -24,6 +24,7 @@
h2
runtime
+
org.springframework.boot
spring-boot-starter-test
@@ -31,5 +32,4 @@
-
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/Application.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/Application.java
new file mode 100644
index 0000000000..94e54889f6
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/Application.java
@@ -0,0 +1,15 @@
+package com.baeldung.spring.data.jpa.naturalid;
+
+import com.baeldung.spring.data.jpa.naturalid.repository.NaturalIdRepositoryImpl;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/HotelRoomsService.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/HotelRoomsService.java
new file mode 100644
index 0000000000..c8cd1fada4
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/HotelRoomsService.java
@@ -0,0 +1,37 @@
+package com.baeldung.spring.data.jpa.naturalid;
+
+import com.baeldung.spring.data.jpa.naturalid.entity.ConferenceRoom;
+import com.baeldung.spring.data.jpa.naturalid.entity.GuestRoom;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+
+import org.hibernate.Session;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+@Service
+public class HotelRoomsService {
+
+ private final EntityManager entityManager;
+
+ public HotelRoomsService(EntityManagerFactory entityManagerFactory) {
+ this.entityManager = entityManagerFactory.createEntityManager();
+ }
+
+ public Optional conferenceRoom(String name) {
+ Session session = entityManager.unwrap(Session.class);
+ return session.bySimpleNaturalId(ConferenceRoom.class)
+ .loadOptional(name);
+ }
+
+ public Optional guestRoom(int roomNumber, int floor) {
+ Session session = entityManager.unwrap(Session.class);
+ return session.byNaturalId(GuestRoom.class)
+ .using("roomNumber", roomNumber)
+ .using("floor", floor)
+ .loadOptional();
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/NaturalIdRepositoryConfig.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/NaturalIdRepositoryConfig.java
new file mode 100644
index 0000000000..94b96ca86c
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/NaturalIdRepositoryConfig.java
@@ -0,0 +1,11 @@
+package com.baeldung.spring.data.jpa.naturalid;
+
+import com.baeldung.spring.data.jpa.naturalid.repository.NaturalIdRepositoryImpl;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+@Configuration
+@EnableJpaRepositories(repositoryBaseClass = NaturalIdRepositoryImpl.class)
+public class NaturalIdRepositoryConfig {
+}
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/entity/ConferenceRoom.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/entity/ConferenceRoom.java
new file mode 100644
index 0000000000..6ff7e48a3d
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/entity/ConferenceRoom.java
@@ -0,0 +1,49 @@
+package com.baeldung.spring.data.jpa.naturalid.entity;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+
+import org.hibernate.annotations.NaturalId;
+
+@Entity
+public class ConferenceRoom {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NaturalId
+ private String name;
+
+ private int capacity;
+
+ public ConferenceRoom(String name, int capacity) {
+ this.name = name;
+ this.capacity = capacity;
+ }
+
+ protected ConferenceRoom() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getCapacity() {
+ return capacity;
+ }
+
+ public void setCapacity(int capacity) {
+ this.capacity = capacity;
+ }
+
+ @Override
+ public String toString() {
+ return "HotelRoom{" + "id=" + id + ", name='" + name + '\'' + ", capacity=" + capacity + '}';
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/entity/GuestRoom.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/entity/GuestRoom.java
new file mode 100644
index 0000000000..1219178625
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/entity/GuestRoom.java
@@ -0,0 +1,67 @@
+package com.baeldung.spring.data.jpa.naturalid.entity;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+
+import org.hibernate.annotations.NaturalId;
+
+@Entity
+public class GuestRoom {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NaturalId
+ private Integer roomNumber;
+
+ @NaturalId
+ private Integer floor;
+
+ private String name;
+ private int capacity;
+
+ public GuestRoom(int roomNumber, int floor, String name, int capacity) {
+ this.roomNumber = roomNumber;
+ this.floor = floor;
+ this.name = name;
+ this.capacity = capacity;
+ }
+
+ protected GuestRoom() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public Integer getRoomNumber() {
+ return roomNumber;
+ }
+
+ public Integer getFloor() {
+ return floor;
+ }
+
+ public int getCapacity() {
+ return capacity;
+ }
+
+ public void setCapacity(int capacity) {
+ this.capacity = capacity;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "GuestRoom{" + "id=" + id + ", roomNumber=" + roomNumber + ", floor=" + floor + ", name=" + name + ", capacity=" + capacity + '}';
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/ConferenceRoomRepository.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/ConferenceRoomRepository.java
new file mode 100644
index 0000000000..d12695f40f
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/ConferenceRoomRepository.java
@@ -0,0 +1,9 @@
+package com.baeldung.spring.data.jpa.naturalid.repository;
+
+import com.baeldung.spring.data.jpa.naturalid.entity.ConferenceRoom;
+
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ConferenceRoomRepository extends NaturalIdRepository {
+}
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/GuestRoomJpaRepository.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/GuestRoomJpaRepository.java
new file mode 100644
index 0000000000..dd761d6660
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/GuestRoomJpaRepository.java
@@ -0,0 +1,10 @@
+package com.baeldung.spring.data.jpa.naturalid.repository;
+
+import com.baeldung.spring.data.jpa.naturalid.entity.GuestRoom;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface GuestRoomJpaRepository extends JpaRepository {
+}
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/NaturalIdRepository.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/NaturalIdRepository.java
new file mode 100644
index 0000000000..5fe710c0a5
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/NaturalIdRepository.java
@@ -0,0 +1,11 @@
+package com.baeldung.spring.data.jpa.naturalid.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.NoRepositoryBean;
+
+import java.util.Optional;
+
+@NoRepositoryBean
+public interface NaturalIdRepository extends JpaRepository {
+ Optional naturalId(ID naturalId);
+}
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/NaturalIdRepositoryImpl.java b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/NaturalIdRepositoryImpl.java
new file mode 100644
index 0000000000..2c6e62e189
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/java/com/baeldung/spring/data/jpa/naturalid/repository/NaturalIdRepositoryImpl.java
@@ -0,0 +1,29 @@
+package com.baeldung.spring.data.jpa.naturalid.repository;
+
+import jakarta.persistence.EntityManager;
+
+import org.hibernate.Session;
+import org.springframework.data.jpa.repository.support.JpaEntityInformation;
+import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
+import org.springframework.data.repository.NoRepositoryBean;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.Serializable;
+import java.util.Optional;
+
+public class NaturalIdRepositoryImpl extends SimpleJpaRepository implements NaturalIdRepository {
+ private final EntityManager entityManager;
+
+ public NaturalIdRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
+ super(entityInformation, entityManager);
+ this.entityManager = entityManager;
+ }
+
+ @Override
+ public Optional naturalId(ID naturalId) {
+ return entityManager.unwrap(Session.class)
+ .bySimpleNaturalId(this.getDomainClass())
+ .loadOptional(naturalId);
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-repo-3/src/main/resources/application.properties
index 8b13789179..5a1841e2ad 100644
--- a/persistence-modules/spring-data-jpa-repo-3/src/main/resources/application.properties
+++ b/persistence-modules/spring-data-jpa-repo-3/src/main/resources/application.properties
@@ -1 +1,2 @@
-
+logging.level.org.hibernate.SQL=DEBUG
+logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
diff --git a/persistence-modules/spring-data-jpa-repo-3/src/test/java/com/baeldung/spring/data/jpa/naturalid/NaturalIdIntegrationTest.java b/persistence-modules/spring-data-jpa-repo-3/src/test/java/com/baeldung/spring/data/jpa/naturalid/NaturalIdIntegrationTest.java
new file mode 100644
index 0000000000..96cc805082
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-repo-3/src/test/java/com/baeldung/spring/data/jpa/naturalid/NaturalIdIntegrationTest.java
@@ -0,0 +1,56 @@
+package com.baeldung.spring.data.jpa.naturalid;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import com.baeldung.spring.data.jpa.naturalid.entity.ConferenceRoom;
+import com.baeldung.spring.data.jpa.naturalid.entity.GuestRoom;
+import com.baeldung.spring.data.jpa.naturalid.repository.ConferenceRoomRepository;
+import com.baeldung.spring.data.jpa.naturalid.repository.GuestRoomJpaRepository;
+
+@SpringBootTest
+class NaturalIdIntegrationTest {
+
+ @Autowired
+ private HotelRoomsService service;
+
+ @Autowired
+ private GuestRoomJpaRepository guestRoomJpaRepository;
+ @Autowired
+ private ConferenceRoomRepository conferenceRoomRepository;
+
+ @Test
+ void whenWeFindByNaturalKey_thenEntityIsReturnedCorrectly() {
+ guestRoomJpaRepository.save(new GuestRoom(23, 3, "B-423", 4));
+
+ Optional result = service.guestRoom(23, 3);
+
+ assertThat(result).isPresent()
+ .hasValueSatisfying(room -> "B-423".equals(room.getName()));
+ }
+
+ @Test
+ void whenWeFindBySimpleNaturalKey_thenEntityIsReturnedCorrectly() {
+ conferenceRoomRepository.save(new ConferenceRoom("Colorado", 100));
+
+ Optional result = service.conferenceRoom("Colorado");
+
+ assertThat(result).isPresent()
+ .hasValueSatisfying(room -> "Colorado".equals(room.getName()));
+ }
+
+ @Test
+ void givenNaturalIdRepository_whenWeFindBySimpleNaturalKey_thenEntityIsReturnedCorrectly() {
+ conferenceRoomRepository.save(new ConferenceRoom("Nevada", 200));
+
+ Optional result = conferenceRoomRepository.naturalId("Nevada");
+
+ assertThat(result).isPresent()
+ .hasValueSatisfying(room -> "Nevada".equals(room.getName()));
+ }
+}
\ No newline at end of file