까먹었다.. 내일은 꼭 9시에 풀어서 올려야지
2주차, RestTemplate
라이브러리 사용만으로는 구현이 힘든 기능들을 사용할 때
서버에서 다른 서버로 간편하게 요청할 수 있도록 제공하는 기능
client입장의 서버(8080)와 server입장의 서버(7070)를 만듬.
init 세팅 후 커밋
RestTemplate의 Get요청
private final RestTemplate restTemplate;
public RestTemplateService(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
UriComponentsBuilder를 통해 Url을 만들 수 있음.
queryParam : ?= data 하는 방식
postman -> client 서버 -> server 서버
client 서버 : ResponseEntity responseEntity = restTemplate.getForEntity(uri, ItemDto.class);
를 통해 server 서버로 보내준 뒤, server 서버에서 받아서 리턴 해 준다.
데이터 여러 개 가져오기
client 서버에서 gradle에 json 추가
fromJSONtoItems 추가 : 중첩 json형식으로 넘어온 데이터를 ItemDto로 변환 후 반환
ResponseDto 변환할 때 생성자에 데이터 넣어서 변환한거처럼 같은 방식으로
해서 itemJson.getString(key:"title") 이런 방식으로 가져온다.
Json은 key value 형식이니까!
public List<ItemDto> fromJSONtoItems(String responseEntity) {
JSONObject jsonObject = new JSONObject(responseEntity);
JSONArray items = jsonObject.getJSONArray("items");
List<ItemDto> itemDtoList = new ArrayList<>();
for (Object item : items) {
ItemDto itemDto = new ItemDto((JSONObject) item);
itemDtoList.add(itemDto);
}
return itemDtoList;
}
RestTemplate Post 요청
RestTemplate Exchange
// 클라이언트 서버
public List<ItemDto> exchangeCall(@RequestHeader("Authorization") String token) {
}
이런식으로 받아올 수 있음
보내는 건
RequestEntity<User> requestEntity = RequestEntity
.post(uri)
.header("X-Authorization", token)
.body(user);
서버용 서버에서는
// 서버용 서버
public ItemResponseDto exchangeCall(@RequestHeader("X-Authorization") String token, @RequestBody UserRequestDto requestDto) {
return itemService.exchangeCall(token, requestDto);
}
반환하는건
public ItemResponseDto exchangeCall(String token, UserRequestDto requestDto) {
System.out.println("token = " + token);
System.out.println("requestDto.getUsername() = " + requestDto.getUsername());
System.out.println("requestDto.getPassword() = " + requestDto.getPassword());
return getCallList();
}
테스트 하는 포스트맨에서는
http://localhost:8080/api/client/exchange-call 이후 Header에 key(Authorization) 와 value를 넣어줘야 한다.
네이버 오픈 API
중간부분이 spring-resttemplet-client 서버 만들것!
public List<ItemDto> searchItems(String query) {
// 요청 URL 만들기
URI uri = UriComponentsBuilder
.fromUriString("https://openapi.naver.com")
.path("/v1/search/shop.json")
.queryParam("display", 15) // default : 10
.queryParam("query", query)
.encode()
.build()
.toUri();
log.info("uri = " + uri);
RequestEntity<Void> requestEntity = RequestEntity
.get(uri)
.header("X-Naver-Client-Id", "Client-Id")
.header("X-Naver-Client-Secret", "Client-Secret")
.build();
ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class);
log.info("NAVER API Status Code : " + responseEntity.getStatusCode());
return fromJSONtoItems(responseEntity.getBody());
}
Entity 연관 관계
서로 상대의 Entity 참조 : 양방향
한쪽이라도 참조하지 못함 : 단방향
1 대 1 관계
@OneToOn
단뱡향 / 양방향 존재
단방향일 때 외래키의 주인만 상대 Entity의 필드를 가짐(@JoinColumn)
jpa에서는 양방향일때 외래키(@JoinColumn)의 주인을 알려줘야함
외래키의 주인이 아닌 쪽에서 지정해야함(@mappedBy)
@mappedBy : 상대 Entity의 필드명. 본인 클래스 명이 아니다.
save() 안에는
내부적으로 트랜잭션이 있어서 insert가 가능한 상태.
변경감지 하려면 @Transaction을 걸어줘야 작동한다! (영속성 컨텍스트)
외래키의 주인만이 외래키를 컨트롤 할 수 있다.
근데? addFood()라는 메서드를 만들어서 주인이 아닌쪽에서도 저장할 수 있다.(우회)
= 조회는 가능 하다
N 대 1 관계
@Test
@Rollback(value = false)
@DisplayName("N대1 단방향 테스트")
void test1() {
User user = new User();
user.setName("Robbie");
Food food = new Food();
food.setName("후라이드 치킨");
food.setPrice(15000);
food.setUser(user); // 외래 키(연관 관계) 설정
Food food2 = new Food();
food2.setName("양념 치킨");
food2.setPrice(20000);
food2.setUser(user); // 외래 키(연관 관계) 설정
userRepository.save(user);
foodRepository.save(food);
foodRepository.save(food2);
}
1 대 N 관계
public class Food {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@OneToMany
@JoinColumn(name = "food_id") // users 테이블에 food_id 컬럼
// user_id 가 아닌것에 주의
private List<User> userList = new ArrayList<>();
}
N 대 M 관계
@ManyToMany
@JoinTable(name = "orders", // 중간 테이블 생성
joinColumns = @JoinColumn(name = "food_id"), // 현재 위치인 Food Entity 에서 중간 테이블로 조인할 컬럼 설정
inverseJoinColumns = @JoinColumn(name = "user_id")) // 반대 위치인 User Entity 에서 중간 테이블로 조인할 컬럼 설정
private List<User> userList = new ArrayList<>();
= 고객은 주문을 여러번 할 수 있다.
= public class User {
@OneToMany(mappedBy = "user")
private List<Order> orderList = new ArrayList<>();
}
이게 이 뜻이다.
= 음식은 주문을 여러번 될 수 있다.
= public class 음식 {
@ManyToOne
@JoinColumn(name = "food_id")
private Food food;
}
이런식으로
처음에 오는게 현재 엔티디 / 다음에 오는게 상대 엔티티.
지연 로딩
Jpa 에서는 데이터를 가져올 때 FetchType이라는 가져오는 방법이 있는데,
LAZY : 지연 로딩
필요한 시점의 정보를 로딩
EAGER : 즉시 로딩
데이터를 즉시 로딩
어노테이션 이름중에 Many가 있으면 LAZY가 디폴트
반대로 One이라면 즉시 정보를 로딩해도 무리가 없으니 EAGER가 디폴트
영속성 컨텍스트 : 1차 캐시 / 쓰기 지연 저장소 / 변경 감지
지연로딩도 영속성 컨텍스트의 기능 중 하나.
Select 할 땐 영속성 컨텍스트가 필수가 아니지만,
지연 로딩 된 정보를 조회하기 위해서는 영속성 컨텍스트가 있어야 한다.
성공 = 트렌잭션 : 공식!
영속성 전이
Cascade Persist
영속성 배울때 delete 할 때 연관된 데이터 삭제 불가능 했었던 거
onCascade? 해서 연관된 데이터 삭제 했었다.
지금 하려는건 foodlist에 넣었던 것 전파되서 한번에 저장하려고 한다.( 로비 음식 저장 -> 영속성 전이 저장으로)
영속성 전이 저장 :
@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST)
영속성 전이 삭제 :
@OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
고아 Entity 삭제
Jpa에서는 연관관계를 지우는 것만으로도 해당 데이터를 지울 수 있다.
@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST, orphanRemoval = true)
이 옵션도 Cascade.REVOCE 처럼 Robbie를 삭제하면 연관된 음식 Entity를 삭제하는 기능도 같이 가지고 있다.
삭제를 한 후에 다른곳에서 참조되고 있었는지 확인 해주어야 한다. 다른 객체가 있을 수 있기 때문에.