이 번장에서는, 앞장에서 페이징 처리한 데이터 결과를 RestAPI를 통해
데이터베이스의 테이블 데이터를,검색 API화 하는것을 진행해보겠습니다.
페이징할 Entity체크
User | Group |
---|---|
@Entity public class User { @Id @GeneratedValue @Column(name = "USER_ID") private Integer id; private String name; private String email; @ManyToOne @JoinColumn(name = "GROUP_ID") private GroupInfo groupInfo; | @Entity public class GroupInfo { @Id @GeneratedValue @Column(name = "GROUP_ID") private Integer id; private String name; @OneToMany(mappedBy = "groupInfo", cascade = {CascadeType.PERSIST},fetch=FetchType.EAGER) private Set<User> users; |
우리는, List<User> 를 Json화 하여 데이터를 뿌려줄것입니다. 하지만 위 Entity 구조에 어떠한게 문제가 있을까요?
User에서는 ManyToOne으로 Group정보를 하나를 참조하고, GroupInfo에서는 OnToMay로 다시 User객체를 다수개 참조 하려할것입니다.
결국 참조를 무한으로 재귀적으로 반복하는, 무한 루프에 빠지게 됩니다.
List<GroupInfo>를 리스트화하여 뿌려준다면, 그룹별 사용자 리스트가 올바르게 표현이 될것이나....
이 케이스에서는 Group 에 포함된 사용자 리스트가 Json화 안되게끔 무시 플래그를 넣어야합니다.
JPA를 통해 Table - CLASS -Json 3가지 성격이 다른 엔티티를 통합하여 일괄적인 처리가 될수있게 하였지만
양방향 참조는 JSON에 위배됩니다.(재귀적 무한루프)
JSON표현해 한해 단방향으로 만들수 있습니다.
관련이슈에대한 토론:
다시 역참조가 안되게, GroupInfo의 users의 참조를 끊어주겠습니다.( @JsonBackReference 동일한 효과)
@Entity public class GroupInfo { @Id @GeneratedValue @Column(name = "GROUP_ID") private Integer id; private String name; @JsonIgnore @OneToMany(mappedBy = "groupInfo", cascade = {CascadeType.PERSIST},fetch=FetchType.EAGER) private Set<User> users;
UserPageRepo 개선
public interface UserPageRepo extends Repository<User, Long>{ Page<User> findByNameContaining(String name,Pageable pageRequest); }
UserPagerepo는 앞장에서 설명을 하였으며, 사용자의 이름이 포함된 검색을 하고 페이징처리가 되는 인터페이스를
추가하겠습니다. 구현은 필요없으며,, 지정된 함수명에따라 아래와같이 SQL문과 동일한 효과의 기능이 수행됩니다.
- findByName : where name = ??
- findByNameContaining : where name like '%??%'
STS(Spring Tool Suite) 에서, 입력한 키워드에서 컨트롤+Space를 누르면, 쿼리기능에 관련된
인터페이스가 자동완성으로 확인가능하며..., 어떠한 SQL문이 수행될것이다라고 유추할수가 있습니다.
UserSearchAPI 작성
package com.example.demo; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping(path="/") //이 클래스의 루트 Router를 설정한다. public class UserSearchAPI { @Autowired private UserPageRepo userPageRepo; @GetMapping(path="/usersearch") //이 멤버함수의 하위 주소를 설정한다. public @ResponseBody List<User> userSearch( @RequestParam String name, @RequestParam int pagenum, @RequestParam int pagesize ){ PageRequest pageReq = new PageRequest(pagenum, pagesize); Page<User> userPage =userPageRepo.findByNameContaining(name,pageReq); return userPage.getContent(); } }
RestAPI는 처음 소개되는거라, 해당 Class의 Full소스를 가지고 설명하겠습니다.
각 어노테이션은 아래와 같은 역활을 합니다.
- Controller : 해당 클래스가 WebAPI의 역활을 하는것을 명시
- RequestMapping : 해당클래스의 루트 Route를 설정합니다.
- GetMapping : 실제 기능이 작동하는 함수에 하위 Route를 설정합니다.
- ResponseBody : 일반적인 웹 컨텐를 반환하겠다란 의미
위 작성된 REST API는 아래와 같이 검색 API의 기본기능을 갖추게 됩니다.
http://localhost:8080/usersearch?name=minsu&pagenum=0&pagesize=5 ==> 웹결과 [{"id":1,"name":"minsu7","email":"min7@x.com","groupInfo":{"id":1,"name":"학생"}}, {"id":2,"name":"minsu11","email":"min11@x.com","groupInfo":{"id":1,"name":"학생"}}, {"id":3,"name":"minsu75","email":"min75@x.com","groupInfo":{"id":1,"name":"학생"}}, {"id":4,"name":"minsu80","email":"min80@x.com","groupInfo":{"id":1,"name":"학생"}}, {"id":5,"name":"minsu83","email":"min83@x.com","groupInfo":{"id":1,"name":"학생"}}]