Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contribute to Class Based Projection Document #3754

Open
ogunodabasss opened this issue Jan 28, 2025 · 4 comments
Open

Contribute to Class Based Projection Document #3754

ogunodabasss opened this issue Jan 28, 2025 · 4 comments
Labels
status: feedback-provided Feedback has been provided

Comments

@ogunodabasss
Copy link

ogunodabasss commented Jan 28, 2025

I want to add a feature that is not published by the documentation.

This feature is only valid for JPA queries. We can choose the columns we want according to the DTO model. An alternative to interface based projection.

If you want to check it out, I share the medium and github links:
medium
github

In code, the feature is as follows:

Entity 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() + "]";
}
*/}

Entity 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 AdressDTO1:
@Data @AllArgsConstructor public class AdressDTO1 { private Long id; private int no; private String house; }

DTO UserDto1:

@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);
}}

//Repository Class Method: public <T> List<T> findBy(Class<T> clazz);
//Service Layer Method: userRepository.findBy(UserDto1.class);

Select Query:

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

@christophstrobl
Copy link
Member

If you would like us to spend some time helping you with the issue at hand, please spend some time describing it. Provide proper formatted code, links that don't return 404, etc. and tell us what you want to achieve.

@christophstrobl christophstrobl added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 28, 2025
@ogunodabasss
Copy link
Author

I rearranged the link and code part.

In the documentation, dto based projection is mentioned only for a single Entity model. In other words, table relationships with table structure OneToOne and ManyToOne are not mentioned.

According to the created DTO model (Example: UserDto1 and AdressDTO1), even if the FetchType is Lazy, we can only retrieve the fields we want from the related table in the query and it does not cause the n+1 query problem. You don't have to write a select query (select c1,c2,c3,c5,c6 from ...). It only supports JPA queries. It supports OneToOne and ManyToOne table structure.

There are methods like @EntityGrapht, but these solutions are static. I find this DTO based projection useful because it brings dynamism. I would like to contribute to your documentation of this method I have been researching.

It works just like interface based projections, but since interface based projections have lower performance, DTO based is a much faster and more dynamic solution.

This topic is where I want to contribute.

Additionally, the use of interface based projections for dynamic queries results in excessive CPU usage in the system and GC usage in Java. You can look at the picture where I measured the sample with VisualVM. The point I marked in red is that the interface based projection query is the slowest compared to all other queries for the same select query.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jan 28, 2025
@mp911de
Copy link
Member

mp911de commented Jan 31, 2025

I am not sure I understand. Are you looking for a way to automatically select properties from relations like address.house into DTO projections, given the constructor argument name is addressHouse?

@ogunodabasss
Copy link
Author

Automatically selecting features, actually Spring already does this. Since I discovered this feature, I thought it should be mentioned in the documentation.

For example:
According to the User and Address classes I shared.
When AdressDto2, which I gave as an example, is changed instead of AdressDto1 in the UserDto1 class; The generated query would be like this:

@Data @AllArgsConstructor public class AdressDto2 { private Long id; private String house; }

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_.house as col_4_0_
from
  user user0_
  left outer join
  Adress Adress1_
  on user0_.adress_id=adress1_.id

In this way DTO according to the model. It selects features automatically. All that needs to be done is:

userRepository.findBy(UserDto1.class);
userRepository.findBy(UserDto2.class);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: feedback-provided Feedback has been provided
Projects
None yet
Development

No branches or pull requests

4 participants