이 번장에서는, 앞장에서 페이징 처리한 데이터 결과를 RestAPI를 통해

데이터베이스의 테이블 데이터를,검색 API화 하는것을 진행해보겠습니다.


페이징할 Entity체크

UserGroup
@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":"학생"}}]











  • No labels