Spring Data Supports Interface Based Projections and class-based projections which are used as a return type to retrieve the required attributes, but both interface and class-based projections have the limitation that they cannot be changed the return type at the run time.
Dynamic projections help us to change the return types at run time.
drop database if exists AppleDb;
create database AppleDb;
use AppleDb;
create TABLE apple ( apple_id BIGINT NOT NULL, apple_name VARCHAR(255) DEFAULT NULL, taste VARCHAR(255) DEFAULT NULL, PRIMARY KEY (apple_id)) ENGINE=INNODB;
create TABLE hibernate_sequence ( next_val BIGINT) ENGINE=INNODB;
insert into hibernate_sequence values ( 1 );
insert into apple (apple_name, apple_id, taste) values ('Macintosh', 1, 'tangy');
insert into apple (apple_name, apple_id, taste) values ('Fuji', 2, 'sweet');
insert into apple (apple_name, apple_id, taste) values ('Gala', 3, 'tangy');
insert into apple (apple_name, apple_id, taste) values ('Jonagold', 4, 'sweet');
insert into apple (apple_name, apple_id, taste) values ('GrannySmith', 5, 'tangy');
package com.sujan.example.jpa.controller;
import com.sujan.example.jpa.entity.Apple;
import com.sujan.example.jpa.entity.AppleIdAndTasteSummary;
import com.sujan.example.jpa.entity.AppleNameAndTasteSummary;
import com.sujan.example.jpa.entity.AppleSummary;
import com.sujan.example.jpa.repository.AppleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
@RestController
public class AppleController {
@Autowired
private AppleRepository appleRepository;
@GetMapping
void projection() {
Collection<Apple> apple = appleRepository.findByTaste("tangy", Apple.class);
apple.forEach(System.out::println);
Collection<AppleIdAndTasteSummary> appleIdAndTasteSummary = appleRepository.findByTaste("tangy", AppleIdAndTasteSummary.class);
appleIdAndTasteSummary.forEach(System.out::println);
Collection<AppleNameAndTasteSummary> appleNameAndTasteSummary = appleRepository.findByTaste("tangy", AppleNameAndTasteSummary.class);
appleNameAndTasteSummary.forEach(System.out::println);
Collection<AppleSummary> AppleSummary = appleRepository.findByTaste("tangy", AppleSummary.class);
AppleSummary.forEach(System.out::println);
}
}
package com.sujan.example.jpa.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Data
@Entity
@ToString
@NoArgsConstructor
@AllArgsConstructor()
public class Apple {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long appleId;
private String appleName;
private String taste;
}
package com.sujan.example.jpa.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.Entity;
@Data
@Entity
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class AppleIdAndTasteSummary {
private Long appleId;
private String taste;
}
package com.sujan.example.jpa.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.Entity;
@Data
@Entity
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class AppleNameAndTasteSummary {
private String appleName;
private String taste;
}
package com.sujan.example.jpa.entity;
import lombok.Value;
@Value
public class AppleSummary {
private Long appleId;
private String appleName;
private String taste;
}
package com.sujan.example.jpa.repository;
import com.sujan.example.jpa.entity.Apple;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Collection;
@Repository
public interface AppleRepository extends JpaRepository<Apple, Long> {
<T> Collection<T> findByTaste(String taste, Class<T> type);
}
package com.sujan.example.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JpaApplication {
public static void main(String[] args) {
SpringApplication.run(JpaApplication.class, args);
}
}
spring.datasource.url=jdbc:mysql://localhost:3306/AppleDb?autoReconnect=true&useSSL=false&createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
spring.datasource.initialization-mode=always
plugins {
id 'org.springframework.boot' version '2.3.3.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.sujan'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
annotationProcessor 'org.projectlombok:lombok'
compileOnly 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
curl --location --request GET 'http://localhost:8080/'
select apple0_.apple_id as apple_id1_0_, apple0_.apple_name as apple_na2_0_, apple0_.taste as taste3_0_ from apple apple0_ where apple0_.taste=?
Apple(appleId=1, appleName=Macintosh, taste=tangy)
Apple(appleId=3, appleName=Gala, taste=tangy)
Apple(appleId=5, appleName=GrannySmith, taste=tangy)
Hibernate: select apple0_.apple_id as col_0_0_, apple0_.taste as col_1_0_ from apple apple0_ where apple0_.taste=?
AppleIdAndTasteSummary(appleId=1, taste=tangy)
AppleIdAndTasteSummary(appleId=3, taste=tangy)
AppleIdAndTasteSummary(appleId=5, taste=tangy)
Hibernate: select apple0_.apple_name as col_0_0_, apple0_.taste as col_1_0_ from apple apple0_ where apple0_.taste=?
AppleNameAndTasteSummary(appleName=Macintosh, taste=tangy)
AppleNameAndTasteSummary(appleName=Gala, taste=tangy)
AppleNameAndTasteSummary(appleName=GrannySmith, taste=tangy)
Hibernate: select apple0_.apple_id as col_0_0_, apple0_.apple_name as col_1_0_, apple0_.taste as col_2_0_ from apple apple0_ where apple0_.taste=?
AppleSummary(appleId=1, appleName=Macintosh, taste=tangy)
AppleSummary(appleId=3, appleName=Gala, taste=tangy)
AppleSummary(appleId=5, appleName=GrannySmith, taste=tangy)