0

I have a springboot application managing a User, UserProfile and Roles
A user can have one profile only (i want a unidirectional relationship, i only need to navigate from User -> UserProfile)
A User can have many "Roles" (authorities), i want a ManyToMany relationship
Here is the code for the entities :
User

@AllArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "users")
public class User extends AbstractAuditingEntity<Long> {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "user_id")
  private Long id;

  @NotBlank
  @Size(max = 15)
  private String username;

  @NaturalId
  @NotBlank
  @Size(max = 40)
  @Email
  private String email;

  @NotBlank
  @Size(max = 100)
  private String password;

  @ManyToMany(fetch = FetchType.LAZY)
  @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
  private Set<Role> roles = new HashSet<>();

  @OneToOne(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  @JsonManagedReference
  private UserProfile userProfile;
}

UserProfile (@OneToOne with User)

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name="user_profile")
public class UserProfile {

    @Id
    @Column(name="user_id")
    private Long id;
    
    @Column(name="first_name")
    private String firstName;

    @Column(name="last_name")
    private String lastName;

    @Column(name = "phone_number")
    @Size(max = 15)
    private String phoneNumber;

    @OneToOne
    @MapsId
    @JoinColumn(name = "user_id")
    @JsonBackReference
    private User user;

}

Role (@ManyToMany with User)

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name="roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="role_id")
    private Long id;

    @Enumerated(EnumType.STRING)
    @NaturalId
    @Column(length = 60)
    private RoleName name;
}


I'm trying to get a User with it's UserProfile only via a DTO class :

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserWithProfileDto {
    private String username;
    private String email;
    private String password;
    private UserProfile userProfile;
}

The query is as follows :

    @Query("""
      select new com.springboot.jwtstarter.domain.dto.UserRolesDto
      (u.username, u.email, u.password, u.userProfile)
       from User u 
       where u.id = (:id)
    """)
    Optional<UserRolesDto> findUserRoleDtoById(Long id);

i want to do this in one query but the console shows me two queries :

select u1_0.username,u1_0.email,u1_0.password,u2_0.user_id,u2_0.first_name,u2_0.last_name,u2_0.phone_number from users u1_0 join user_profile u2_0 on u1_0.user_id=u2_0.user_id where u1_0.user_id=?

 select u1_0.user_id,u1_0.created_by,u1_0.created_date,u1_0.email,u1_0.last_modified_by,u1_0.last_modified_date,u1_0.password,u1_0.username from users u1_0 where u1_0.user_id=?

I want to clarify the following points:

  1. How to execute the above query in One database hit with a join ?
  2. How to do the same with a User and it's roles only ?

EDIT: i'm able to resolve questions 1 & 2 using projections like as follows (example with roles):

    @Query("""
      select 
      u.username as username,
      u.email as email,
      u.password as password,
      u.roles as roles
      from User u
      where u.id = :id
    """)
    Optional<UserRolesI> findUserRoleDtoByIdInterface(Long id);
public interface UserRolesI {

    String getUsername();
    String getEmail();
    Set<Role> getRoles();
}

the generated hibernate query is :

select u1_0.username,u1_0.email,u1_0.password,u2_0.user_id,u2_0.first_name,u2_0.last_name,u2_0.phone_number from users u1_0 join user_profile u2_0 on u1_0.user_id=u2_0.user_id where u1_0.user_id=?

Which is one query using one join, but that doesn't happen when using the DTO as described above, any idea ?

user7616817
  • 357
  • 4
  • 18

0 Answers0