diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/model/Address.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/model/Address.java
new file mode 100644
index 0000000000..0c5a3eac60
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/model/Address.java
@@ -0,0 +1,57 @@
+package com.baeldung.projection.model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+
+@Entity
+public class Address {
+ @Id
+ private Long id;
+ @OneToOne
+ private Person person;
+ private String state;
+ private String city;
+ private String street;
+ private String zipCode;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ 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 String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/model/Person.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/model/Person.java
new file mode 100644
index 0000000000..d18bd1c72d
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/model/Person.java
@@ -0,0 +1,47 @@
+package com.baeldung.projection.model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+
+@Entity
+public class Person {
+ @Id
+ private Long id;
+ private String firstName;
+ private String lastName;
+ @OneToOne(mappedBy = "person")
+ private Address address;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long 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/spring-data-jpa-2/src/main/java/com/baeldung/projection/repository/AddressRepository.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/repository/AddressRepository.java
new file mode 100644
index 0000000000..c1053f4867
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/repository/AddressRepository.java
@@ -0,0 +1,11 @@
+package com.baeldung.projection.repository;
+
+import com.baeldung.projection.view.AddressView;
+import com.baeldung.projection.model.Address;
+import org.springframework.data.repository.Repository;
+
+import java.util.List;
+
+public interface AddressRepository extends Repository
{
+ List getAddressByState(String state);
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/repository/PersonRepository.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/repository/PersonRepository.java
new file mode 100644
index 0000000000..64bc7471e6
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/repository/PersonRepository.java
@@ -0,0 +1,14 @@
+package com.baeldung.projection.repository;
+
+import com.baeldung.projection.model.Person;
+import com.baeldung.projection.view.PersonDto;
+import com.baeldung.projection.view.PersonView;
+import org.springframework.data.repository.Repository;
+
+public interface PersonRepository extends Repository {
+ PersonView findByLastName(String lastName);
+
+ PersonDto findByFirstName(String firstName);
+
+ T findByLastName(String lastName, Class type);
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/AddressView.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/AddressView.java
new file mode 100644
index 0000000000..7a24a1e9b9
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/AddressView.java
@@ -0,0 +1,7 @@
+package com.baeldung.projection.view;
+
+public interface AddressView {
+ String getZipCode();
+
+ PersonView getPerson();
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/PersonDto.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/PersonDto.java
new file mode 100644
index 0000000000..1fd924450b
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/PersonDto.java
@@ -0,0 +1,34 @@
+package com.baeldung.projection.view;
+
+import java.util.Objects;
+
+public class PersonDto {
+ private final String firstName;
+ private final String lastName;
+
+ public PersonDto(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PersonDto personDto = (PersonDto) o;
+ return Objects.equals(firstName, personDto.firstName) && Objects.equals(lastName, personDto.lastName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(firstName, lastName);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/PersonView.java b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/PersonView.java
new file mode 100644
index 0000000000..36777ec26f
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/main/java/com/baeldung/projection/view/PersonView.java
@@ -0,0 +1,12 @@
+package com.baeldung.projection.view;
+
+import org.springframework.beans.factory.annotation.Value;
+
+public interface PersonView {
+ String getFirstName();
+
+ String getLastName();
+
+ @Value("#{target.firstName + ' ' + target.lastName}")
+ String getFullName();
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/projection/JpaProjectionIntegrationTest.java b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/projection/JpaProjectionIntegrationTest.java
new file mode 100644
index 0000000000..96eaf4ed07
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/test/java/com/baeldung/projection/JpaProjectionIntegrationTest.java
@@ -0,0 +1,63 @@
+package com.baeldung.projection;
+
+import com.baeldung.projection.model.Person;
+import com.baeldung.projection.repository.AddressRepository;
+import com.baeldung.projection.repository.PersonRepository;
+import com.baeldung.projection.view.AddressView;
+import com.baeldung.projection.view.PersonDto;
+import com.baeldung.projection.view.PersonView;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
+
+@DataJpaTest
+@RunWith(SpringRunner.class)
+@Sql(scripts = "/projection-insert-data.sql")
+@Sql(scripts = "/projection-clean-up-data.sql", executionPhase = AFTER_TEST_METHOD)
+public class JpaProjectionIntegrationTest {
+ @Autowired
+ private AddressRepository addressRepository;
+
+ @Autowired
+ private PersonRepository personRepository;
+
+ @Test
+ public void whenUsingClosedProjections_thenViewWithRequiredPropertiesIsReturned() {
+ AddressView addressView = addressRepository.getAddressByState("CA").get(0);
+ assertThat(addressView.getZipCode()).isEqualTo("90001");
+
+ PersonView personView = addressView.getPerson();
+ assertThat(personView.getFirstName()).isEqualTo("John");
+ assertThat(personView.getLastName()).isEqualTo("Doe");
+ }
+
+ @Test
+ public void whenUsingOpenProjections_thenViewWithRequiredPropertiesIsReturned() {
+ PersonView personView = personRepository.findByLastName("Doe");
+ assertThat(personView.getFullName()).isEqualTo("John Doe");
+ }
+
+ @Test
+ public void whenUsingClassBasedProjections_thenDtoWithRequiredPropertiesIsReturned() {
+ PersonDto personDto = personRepository.findByFirstName("John");
+ assertThat(personDto.getFirstName()).isEqualTo("John");
+ assertThat(personDto.getLastName()).isEqualTo("Doe");
+ }
+
+ @Test
+ public void whenUsingDynamicProjections_thenObjectWithRequiredPropertiesIsReturned() {
+ Person person = personRepository.findByLastName("Doe", Person.class);
+ PersonView personView = personRepository.findByLastName("Doe", PersonView.class);
+ PersonDto personDto = personRepository.findByLastName("Doe", PersonDto.class);
+
+ assertThat(person.getFirstName()).isEqualTo("John");
+ assertThat(personView.getFirstName()).isEqualTo("John");
+ assertThat(personDto.getFirstName()).isEqualTo("John");
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-2/src/test/resources/projection-clean-up-data.sql b/persistence-modules/spring-data-jpa-2/src/test/resources/projection-clean-up-data.sql
new file mode 100644
index 0000000000..d34f6f0636
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/test/resources/projection-clean-up-data.sql
@@ -0,0 +1,2 @@
+DELETE FROM address;
+DELETE FROM person;
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-2/src/test/resources/projection-insert-data.sql b/persistence-modules/spring-data-jpa-2/src/test/resources/projection-insert-data.sql
new file mode 100644
index 0000000000..544dcc4b88
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-2/src/test/resources/projection-insert-data.sql
@@ -0,0 +1,2 @@
+INSERT INTO person(id,first_name,last_name) VALUES (1,'John','Doe');
+INSERT INTO address(id,person_id,state,city,street,zip_code) VALUES (1,1,'CA', 'Los Angeles', 'Standford Ave', '90001');
\ No newline at end of file