카테고리 없음

spring data jpa 의 json dto

머룽 2023. 4. 19. 09:55

spring data jpa 의 json

오늘은 spring data jpa를 사용하여 json을 출력해보는 포스팅을 하겠다. 글로버 페치 전략에 즉시로딩이 아닌 지연로딩을 사용하면 영속성 상태가 아닐 경우 데이터를 가져올 때 에러가 발생한다. 그래서 open session in view 이하 osiv를 사용하는데 messageconverter일 경우 즉 json일 경우 그래도 에러가 발생한다.(연관관계가 양방향 일 경우) jackson에서 에러는 내뿜는듯 하다. 계속 순환하는거 같다. 필자가 모르는거 일수도 있다. 그래서 고민하기 시작했다. 흠! 방법은 몇가지 있다. @JsonIgnore, @JsonBackReference, @JsonManagedReference 혹은 JSON Filter, 와 @JsonView 현재까지 알아본 결과 위와 같다. 다른 방법은 나중에 설명 하도록 하고 이번 시간에는 dto를 만들어서 해보겠다. dto(data transfer object)란 말그대로 데이터가 이동하는 객체다. 일단 소스로 보자 boot기준이다.

... <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> </dependency> <dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>0.7.5</version> </dependency> ...
일단 dto가 정신사납지 않게 lombok은 필수로 하자 object을 mapping 시키는? 말이 맞나? 아무튼 modelmapper를 메이븐에 등록하자. 그래야 코드가 깔끔해진다. 물론 java8을 써야. Account class
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {

  @Id
  @GeneratedValue
  @Column(name = "ACCOUNT_ID")
  private Long id;

  private String name;

  @OneToMany(mappedBy = "account")
  private List<Orders> orders;

}
딱히 없다 테스트를 하느라 OneToMany도 같이 넣어놨다. oneToMany의 기본페치 전략은 LAZY다. 또한 ManyToMany도 마찬가지다. orders class
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Orders {

  @Id
  @GeneratedValue
  @Column(name = "ORDER_ID")
  private Long id;

  private String orderName;

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "ACCOUNT_ID")
  private Account account;

}

여기도 Account 클래스와 동일하다. ManyToOne의 기본페치 전략은 즉시로딩이다. OneToOne도 마찬가지다. AccountRepository class
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
}

일반적인 Repository 클래스다. 따로 설명은 안하겠다. AccountDto class
public class AccountDto {

  @Data
  public static class Account{
    private Long id;
    private String name;
  }
}
dto 클래스를 만들었다. AccountDto에 한개의 inner class로 만들었다. 물론 여러개가 될수 있다. 저게 테스트 용도라 필드가 얼마 없지만 만약 많다면 getter setter가 어마어마 할 것이다. 그런데 lombok을 써서 관심사를 집중 할 수 있다. 아무튼! modelmapper를 쓰기 위해 빈으로 등록하자
@Bean
public ModelMapper modelMapper() {
  return new ModelMapper();
}
AccountController class
@RestController
public class AccountController {

  @Autowired
  private AccountRepository accountRepository;

  @Autowired
  private ModelMapper modelMapper;

  @RequestMapping(value = "/accounts")
  public List<AccountDto.Account> accountList() {
    List<Account> accounts = accountRepository.findAll();
    return accounts.stream().map(account -> modelMapper.map(account, AccountDto.Account.class))
      .collect(toList());
  }

  @RequestMapping(value = "/account/{id}")
  public AccountDto.Account account(@PathVariable Long id){
    Account account = accountRepository.findOne(id);
    return modelMapper.map(account, AccountDto.Account.class);
  }
}
간단한 테스트용도라 서비스르 없애버렸다. spring boot의 기본전략이 open session in view를 적용한 듯하다. 그래서 서비스계층을 없앴다. modelmapper로 기존 account를 accountDto로 변경하여 뷰로 보내줬다. 이러면 뷰에선 id와 name만을 볼 수 있다. 글쎄다. 뭐가 좋은지 어떤게 효율적인지는 필자도 잘 모르겠다. 그때그때 상황을 봐가면서 상황에 맞게 하는게 나을지도 모른다. JPA 책을보면 특수한 경우가 아니라면 페치 전략을 LAZY하는게 성능에 좋다고 한다. 위에서 설명은 하지 않았지만 방법은 적어놨으니 한번 살펴 보자!