Java, IntelliJ/Spring

SPRING,JPA 원하는 값 내려주기 방법 모음 총 정리, Projection 방법 모음

고로케 2021. 8. 1.
반응형

원하는 결과 값에 따른 적절한 방법을 채택해서 사용해보세요!

다만... 실무에서 어떤 방식을 쓰는지는 잘 모르겠어요

아시는분 댓글 달아주세용!

 

저는 처음에 이런 상황이였음..

-참고 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/

 

 

 
반응형

댓글