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:
- How to execute the above query in One database hit with a join ?
- 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 ?