2021-08-30-TIL
2021-08-30-TIL
MyBatis
MyBatis에서 enum type handler 사용
- https://shlee0882.tistory.com/292
MyBatis는 타입 핸들러(TypeHandler)로 자바 타입과 JDBC 타입을 서로 바꾼다. enum의 기본 핸들러는 EnumTypeHandler이고 상수의 이름(name())을 문자열로 저장한다. 선언 순서(ordinal)를 정수로 저장하려면 EnumOrdinalTypeHandler를 지정한다(MyBatis 설정 문서).
1
2
3
4
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="com.example.DeliveryType"/>
</typeHandlers>
DB에 이름도 순서도 아닌 별도 코드("01" 같은)가 저장되어 있다면 BaseTypeHandler<DeliveryType>을 상속해 코드와 상수를 서로 바꾸는 핸들러를 직접 만든다. ordinal 저장은 상수 선언 순서가 바뀌면 기존 데이터의 의미가 바뀌므로 피하는 편이 안전하다.
MySQL
두 개의 테이블 조인하여 업데이트
- https://guisin.net/127
- http://www.gurubee.net/article/79308
MySQL은 여러 테이블을 조인한 결과로 UPDATE할 수 있다(MySQL UPDATE).
1
2
3
UPDATE items, month
SET items.price = month.price
WHERE items.id = month.id;
위처럼 쉼표로 쓴 내부 조인 외에 SELECT에서 쓸 수 있는 조인은 모두 쓸 수 있다(UPDATE a JOIN b ON ... SET ...). 단일 테이블 UPDATE와 달리 다중 테이블 UPDATE에는 ORDER BY와 LIMIT을 쓸 수 없다. 또 같은 테이블을 갱신하면서 서브쿼리로 그 테이블을 직접 조회할 수는 없어서, 이런 경우에 조인 업데이트가 우회로가 된다.
JSON
Java에서 JSON 파싱
- https://codechacha.com/ko/java-parse-json/
- https://stackoverflow.com/questions/24322776/how-to-convert-a-java-object-to-a-jsonobject
Spring Boot 프로젝트라면 이미 들어 있는 Jackson의 ObjectMapper로 충분하다(ObjectMapper Javadoc).
readValue(json, Foo.class): JSON을 객체로 읽는다.writeValueAsString(foo): 객체를 JSON 문자열로 쓴다.readTree(json): 클래스를 만들지 않고JsonNode트리로 읽는다. 외부 응답에서 일부 필드만 꺼낼 때 편하다.convertValue(foo, Map.class): 직렬화와 역직렬화를 이어서 해 주는 변환이다. 객체를Map으로, 또는 그 반대로 바꿀 때 쓴다.
ObjectMapper는 설정을 모두 마친 뒤에 읽고 쓰기 시작한다면 스레드 안전하다. 그래서 요청마다 새로 만들지 않고 빈으로 공유한다.
Serializer, Deserializer
- https://blog.leocat.kr/notes/2021/03/11/jackson-convert-from-to-string-using-jsonserializer
기본 매핑으로 표현이 안 되는 타입은 JsonSerializer<T>를 상속해 serialize(value, gen, serializers)에서 직접 JSON을 쓴다. 필드에 @JsonSerialize(using = ...)를 붙이거나 SimpleModule에 등록해서 적용한다(JsonSerializer Javadoc). 역방향은 JsonDeserializer<T>와 @JsonDeserialize로 같은 구조다. 외부 API가 숫자를 문자열로 보내거나, 객체를 JSON 문자열 하나로 감싸 보내는 경우에 쓴다.
RestTemplate
- https://www.baeldung.com/how-to-use-resttemplate-with-basic-authentication-in-spring
HTTP Basic 인증은 Authorization: Basic <base64(아이디:비밀번호)> 헤더를 보내는 방식이다. Spring 5.1.1부터는 BasicAuthenticationInterceptor를 RestTemplate에 등록하면 요청마다 이 헤더를 붙여 준다. 이미 Authorization 헤더가 있는 요청은 덮어쓰지 않는다(BasicAuthenticationInterceptor Javadoc).
1
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor("user", "password"));
base64는 암호화가 아니므로 Basic 인증은 HTTPS 위에서만 쓴다. 외부 API 연동 전반에서 타임아웃, 재시도, 오류 응답 처리를 어떻게 볼지는 Spring Boot에서 외부 API 연동을 설계할 때 보는 것에 정리했다.
Java
stream(), peek()
- https://ramees.tistory.com/46
peek은 각 요소를 소비하지 않고 들여다보는 중간 연산이다. Stream Javadoc은 이 메서드가 “exists mainly to support debugging”(주로 디버깅을 돕기 위해 존재한다)고 적는다. 중간 연산은 최종 연산이 실행될 때 비로소 계산되므로, collect 같은 최종 연산이 없으면 peek 안의 코드는 한 번도 실행되지 않는다. 그래서 peek에서 객체 상태를 바꾸는 식으로 로직을 넣지 않고, 값을 바꿔야 하면 map을, 부수 효과가 목적이면 forEach를 쓴다.
MyBatis
ResultMap
- https://webdevtechblog.com/mybatis-resultmap%EC%9D%B4%EB%9E%80-854a94df1f78
- https://mybatis.org/mybatis-3/ko/sqlmap-xml.html
MyBatis 문서는 resultMap을 가장 중요하고 강력한 요소라고 소개한다. 컬럼 이름과 프로퍼티 이름이 다르거나, 조인 결과를 객체 그래프로 묶어야 할 때 쓴다(MyBatis 매퍼 XML 문서).
1
2
3
4
5
6
7
8
<resultMap id="roomResult" type="Room">
<id property="id" column="room_id"/>
<result property="location" column="room_location"/>
<collection property="reservations" ofType="Reservation">
<id property="id" column="reserve_id"/>
<result property="checkIn" column="check_in"/>
</collection>
</resultMap>
id와 result는 컬럼 하나를 프로퍼티 하나에, association은 일대일 관계를, collection은 일대다 관계를 매핑한다. id를 정확히 지정해야 조인으로 늘어난 행들을 같은 객체로 묶을 수 있다. 같은 문서는 #{}는 PreparedStatement 파라미터로 안전하게 바인딩되지만 ${}는 문자열을 그대로 SQL에 끼워 넣어 SQL 인젝션 위험이 있다고 경고한다.
Pagination(Paging)
- https://diaryofgreen.tistory.com/83
MyBatis 페이지네이션의 RowBounds와 LIMIT 방식 차이는 2021-06-04 TIL에 정리했다.
댓글
아직 댓글이 없습니다