Umesh Awasthi
Umesh Awasthi

Reputation: 23587

Spring JPA Class-based Projections and Nested projections

I am trying to use the Class-based Projections to fill the data but seems the Spring JPA does not support the nested projection. Here is my entity class:

public class Category extends BaseEntity<String> {

    @Column(unique = true)
    private String code;

    private String externalCode;

    @ManyToOne(cascade = CascadeType.ALL)
    private Category parent;

    ..
}

Here is the DTO class for same:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CategoryDto implements BaseDto, Serializable {
    private String code;
    private String externalCode;
    private CategoryDto parent;
    ..
}

My CategoryRepository

@Query("select new com.easycart.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt,c.parent) FROM Category c where c.code = :code")
        CategoryDto findCategoryByCode(String code);

I can't use the c.parent as the type is Category and not the CategoryDto, also I did not find any option to use the nested projection to fill the parent information for the given entity. Can someone help me with following questions?

  1. Is there a way to achieve this using class based projection?
  2. DO I need to fallback to the option to fill the parent information separately (I don't need lot of information for the parent in the initial load.).
  3. Any other way to achieve this? I don't want to use the interface based projection as initial test showing it's very slow as compare to class based projection.

Upvotes: 2

Views: 3904

Answers (2)

Og&#252;n
Og&#252;n

Reputation: 125

In fact, it is possible to create dynamic select queries with spring data jpa and hibernate. It only supports @OneToOne and @ManyToOne queries. Take a look at array usages for each RDBMS to support @OneToMany queries.

You can check out the benchmark test for more details. hibernate-jpa-class-based-nested-projections-and-speed

A simple @ManyToOne example:

table user:

public class User {
 @Id
 @SequenceGenerator(name = "user_sequence", sequenceName = "sq_user",allocationSize = 1_000)
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
 private Long id;
 @Column
 private String email;
 @Column
 private String password;

 @ManyToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
 @ToString.Include(name = "id")
 private Adress adress;
/*
 @Override
 public String toString() {
  return "User [id=" + id + ", email=" + email + ", password=" + password 
    + ", adress=" + this.getAdress().getId()
    + "]";
 }
*/
}

table adress:

public class Adress {
 @ToString.Include
 @EqualsAndHashCode.Include
 @Id
 @SequenceGenerator(name = "adres_sequence", sequenceName = "sq_adres",allocationSize = 1_000)
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "adres_sequence")
 private Long id;
 @Column
 private Integer no;
 @Column
 private String house;
 
 
 public Adress(Long id) {
  this.id = id;
 }
}

Dto :

@Data
public class UserDto1 {
 private Long id;
 private String email;
 private String password;
 private AdressDTO1 adressDTO;

 public UserDto1(Long id, String email, String password, Long adressId, int adressNo, String adressHouse) {
  this.id = id;
  this.email = email;
  this.password = password;
  this.adressDTO = new AdressDTO1(adressId, adressNo, adressHouse);
 }
}

@Data
@AllArgsConstructor
public class AdressDTO1 {

 private Long id;
 private int no;
 private String house;
}

Repo. and Service Layer :

//Repository Class Method:
public <T> List<T> findBy(Class<T> clazz);

//Service Layer:
userRepository.findBy(UserDto1.class);

output:

select
    user0_.id as col_0_0_,
    user0_.email as col_1_0_,
    user0_.password as col_2_0_,
    adress1_.id as col_3_0_,
    adress1_.no as col_4_0_,
    adress1_.house as col_5_0_ 
from
    user user0_ 
left outer join
    adress adress1_ 
        on user0_.adress_id=adress1_.id

Upvotes: 0

Jens Schauder
Jens Schauder

Reputation: 81930

There is no out of the box support for this in Spring Data JPA. The way to achieve this is to use constructor expressions and ResultTransformer

Upvotes: 3

Related Questions