SPRING,JPA 원하는 값 내려주기 방법 모음 총 정리, Projection 방법 모음
원하는 결과 값에 따른 적절한 방법을 채택해서 사용해보세요!
다만... 실무에서 어떤 방식을 쓰는지는 잘 모르겠어요
아시는분 댓글 달아주세용!
저는 처음에 이런 상황이였음..
-참고 FetchType 디폴트값
OneToMany: LAZY
ManyToMany: LAZY
ManyToOne: EAGER
OneToOne: EAGER
1. 엔티티-칼럼에 @JsonIgnore 어노테이션 달기
아래와 같이 User정보에 password 같은 걸 빼고 보내고 싶을 때,
해당 칼럼에 @JsonIgnore 를 달아주면 Json형식으로 변환하는
series 단계에서 제외된다.
예시)
- User.java
@Entity
public class User {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
@Column(nullable = false)
private String username;
@Column(nullable = false)
@JsonIgnore
private String password;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "Department_Id")
private Department department;
- Department.java
@Entity
@Getter
@NoArgsConstructor
public class Department {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
@Column(nullable = false)
private String teamName;
@Column(nullable = false)
@JsonIgnore
private String address;
- UserRepository.java
public interface UserRepository extends JpaRepository<User,Long> {
}
- 결과 : @JsonIgnore 붙어 있는 부분을 제외한 요소들만 불러와진다.
userRepository.findById(Long user.getId())
>>결과
{
"id": 5,
"username": "고로케",
"Department":{ "id" : 6,
"teamName" : "로켓단"
}
}
2. 스프링 JPA의 EntityGraph 를 사용하면 JPQL 없이 Fetch Join을 할 수 있다.
(Fetch Type : Lazy 일때, 연관 정보 같이 불러오기)
- Fetch Join 의 간편 버전으로 Left Outer Join 를 할 수 있습니다.
User의 엔티티에 Fetch type = Lazy 가 있다면
User를 호출시 해당 정보들이 같이 내려오지 않는다.
public interface UserRepository extends JpaRepository<User,Long> {
List<User> findAll();
}
이때 EntityGraph를 사용하면 간단하게 Left Join 으로 붙일 수 있다.
@EntityGraph(attributePaths = {"department"})
List<User> findAll();
# 다른 방법 들
@EntityGraph(attributePaths = {"department"})
@Query("select m from department m")
List<User> findMemberEntityGraph();
@EntityGraph(attributePaths = {"department"})
List<User> findEntityGraphByUsername(@Param("username") String username);
>>결과
{
"id": 5,
"username": "고로케",
"password" : "CVKWMM25kK"
"Department":{ "id" : 6,
"teamName" : "로켓단",
"address" : "강남구"
}
}
3. repository에서 불러올 때 원하는 정보만 가져오기 Projection 이용
3-1) 일반적으로 모든 정보를 가져오는 경우
- User.java
@Entity
@Getter
@NoArgsConstructor
public class User extends Timestamped {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String password;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "Department_Id")
private Department department;
- Department.java
@Entity
@Getter
@NoArgsConstructor
public class Department {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
@Column(nullable = false)
private String teamName;
@Column(nullable = false)
private String address;
- Controller.java
@ApiOperation(value = "유저 정보 조회", notes = "유저 정보 조회")
@GetMapping("/user/info")
public User userinfo(@AuthenticationPrincipal UserDetailsImpl userDetails) {
if (userDetails != null) {
return userInfoRepository.findByEmail(userDetails.getUser().getEmail())
.orElseThrow(() -> new IllegalArgumentException("회원이 아닙니다."));
} else {
throw new IllegalArgumentException("로그인 하지 않았습니다.");
}
}
- UserRepository
# UserInfoRepository 의 일반적으로 해당 user 불러오기 경우
public interface UserInfoRepository extends JpaRepository<User,Long> {
Optional<User> findByEmail(String email);
}
- 결과
userRepository.findById(Long user.getId())
>>> 결과
{
"username": "고로케"
"Department": { "id" : 6,
"teamName" : "로켓단",
"address" : "여의도동"
}
}
3-2) Projection 방법 : 원하는 요소만 가져오도록 mapping 용 interface 작성
(Interface based Projections)
방법 -1.1 ) Mapping interface 로 User 요소중 원하는 요소를 type get**() 형태로 작성한다.
- UserInfoMapping.java
>> 원하는 요소 get**() 으로 작성
public interface UserInfoMapping {
String getId();
String getUsername();
Department getDepartment();
}
- UserRepository.java
여기서 Optional <User> 가 아니라 방금 만든 interface의 이름을 넣는다.
Optional <UserInfoMapping> findBy ~~~~
public interface UserRepository extends JpaRepository<User,Long> {
Optional<UserInfoMapping> findByEmail(String email);
}
>>> Controller 에서도 반환 타입을 User 가 아니라 UserInfoMapping 으로 해야한다.
@GetMapping("/user/info")
public UserMapping userinfo(@AuthenticationPrincipal UserDetailsImpl userDetails) {
return userInfoRepository.findByEmail(userDetails.getUser().getEmail())
}
>>> 결과
{ // UserInfoMapping 에 해당한 값만 뽑아옴
"id": 5,
"username": "고로케",
"Department": { "id" : 6,
"teamName" : "로켓단",
"address" : "여의도동"
}
}
방법 -1.2 ) User와 @ManyToOne 연결된 Department엔티티의 요소들 중 선택해서 보여주고 싶을 때
mapping 인터페이스 내부에서 원하는 이름으로 Department를 받아서
그 이름의 interface를 아래 만들어서 그 안쪽에는 Department의 꺼내오고 싶은 요소를
타입명 get**() ; 형태로 써주면 끝
>> 원하는 요소 get**() 으로 작성
public interface UserInfoMapping {
String getId();
String getUsername();
Whatever getDepartment();
interface Whatever{
String getTeamName();
}
}
@GetMapping("/user/info")
public UserMapping userinfo(@AuthenticationPrincipal UserDetailsImpl userDetails) {
return userInfoRepository.findByEmail(userDetails.getUser().getEmail())
}
>>> 결과
{
"id": 5,
"username": "고로케",
"Whatever": { // Whatever 가 key 명이 된다.
"teamName" : "로켓단"
}
}
방법 -2 ) 연결된 엔티티의 요소도 원하는 것만 뽑아오고, 이쁘게? 출력해주자!
- UserInfoMapping
엔티티 안에 있는 객체에서 가져올 때는 아래와 같이
default 원하는타입 get원하는변수명() { return get가져올아이템();}
@JsonIgnore
타입 get가져올아이템(); <- return에 작성한 아이템
public interface UserInfoMapping {
String getUsername();
String getEmail();
// Department 객체의 teamName 만 뽑아오기
# 방법 1) 요소의 이름이 엔티티 이름까지 포함하고 있다
String getDepartmentTeamName();
# 방법 2) 요소의 이름 지정하기
default String getWhatEverName() { // 뽑아올 요소의 이름을 지정할 수 있다.
return getDepartmentTeamName(); // 리턴이랑 이름을 같게 하면 안됩니다.
}
@JsonIgnore
String getDepartmentTeamName();
}
- 결과 -2
@GetMapping("/user/info")
public UserMapping userinfo(@AuthenticationPrincipal UserDetailsImpl userDetails) {
return userInfoRepository.findByEmail(userDetails.getUser().getEmail())
}
>>> 결과 - 방법 1 :요소의 이름이 엔티티 이름까지 포함하고 있다
{
"id": 5,
"username": "고로케",
"departmentTeamName" : "로켓단"
}
>>> 결과 - 방법 2 : 지정한 이름으로 값을 보여준다.
{
"id": 5,
"username": "고로케",
"whatEverName" : "로켓단"
}
-> 결과-1 이랑 달리 Department 의 요소를 다 표기하지 않고 department의 원하는 요소만 보여주고,
그 요소를 department : { } 에 묶여있지 않게 한다.
4. DTO 를 이용하여 가공하기 -1
- UserResponseDto.java
User에서 원하는 요소들을 표현해줄 ResponseDto를 작성해보자
원하는 요소들을 정의해주고
public class UserResponseDto {
private String username;
private String teamName;
// 아래 둘 중 적절한 것을 선택해서 쓰면 된다.
public UserResponseDto(User user) {
this.username = user.getUsername();
this.teamName = user.getDepartment().getTeamName();
// 혹은
public UserResponseDto(String username, String teamname){
this.username = username;
this.teamName = teamname;
}
}
가공할 객체인 User 를 UserResponseDto(user) <- 이렇게 안에 넣어주면 됨됨
혹은 UserResponseDto(user.getUsername , user.getDepartment().getTeamName());
상황에 맞게 사용하자
@GetMapping("/user/info")
public UserResponseDto userinfo(@AuthenticationPrincipal UserDetailsImpl userDetails) {
User user = userRepository.findByIduserDetails.getUser().getId());
UserResponseDto userInfo = UserResponseDto(user);
return userInfo;
}
>>> 결과
{
"username": "고로케",
"teamName" : "로켓단"
}
5. DTO 로 가공하기 -2 : DTO Projection 방법
(Class-based Projections (DTOs))
참고자료 및 출처
https://www.bytestree.com/spring/spring-data-jpa-projections-5-ways-return-custom-object/
'Java, IntelliJ > Spring' 카테고리의 다른 글
AWS EC2_Nginx_ Let's Encrypt로 HTTPS 적용하기 (0) | 2021.08.27 |
---|---|
JAVA 이미지 리사이징, 가로세로 크기 줄이기 (0) | 2021.08.24 |
MySQL Datatype 데이터 타입 (0) | 2021.07.20 |
Spring annotation 모음 (0) | 2021.07.19 |
Spring/ SpringBoot 개념 정리 (0) | 2021.07.19 |
댓글