전체 글
-
[Spring] DynamicInsert 사용 이유2024.07.22
-
Optional 클래스의 orElseThrow2024.07.22
-
[Spring] Enum 타입으로 Exception 구현하기2024.07.22
-
ElasticSearch 를 사용하기 위해 SSL/TLS인증서 추가방법2023.11.30
JPA에서 단방향 및 양방향 일대일 관계의 외래키 처리와 지연 로딩 문제
JPA에서 단방향 및 양방향 일대일 관계의 외래키 처리와 지연 로딩 문제
❗️일대일 대상 테이블에 외래키 단방향 관계는 JPA
에서 지원하지 않으며, 양방향 관계만 지원합니다.
[인프런 김영한님 자료]
대상 테이블에 외래키가 존재하는 경우, 프록시 기능의 한계로 인해 지연 로딩으로 설정해도 항상 즉시 로딩됩니다.
예를 들어, Member
객체를 가져왔을 때 JPA
는 가져온 Member
객체에 Locker
가 있는지 여부를 알아야 합니다.
1. 외래키가 Locker
에 있을 때
외래키가 Locker
에 있다면, Member
엔티티를 가져올 때 Member
클래스에 정의된 Locker
가 존재하는지 확인해야 합니다. 이때 Member
테이블에는 외래키가 존재하지 않기 때문에, 지연 로딩 설정 여부와 상관없이 Locker
테이블을 조회하여 Member
와 연결된 Locker
가 있는지 확인하는 추가 쿼리가 발생합니다.
2. 외래키가 Member
에 있을 때
외래키가 Member
에 있다면, Member
테이블의 조회만으로도 Member
와 연결된 Locker
가 있는지 여부를 판단할 수 있습니다. 따라서 이 경우에는 지연 로딩(lazy loading) 또는 즉시 로딩(eager loading) 설정이 가능합니다.
요약
- 외래키가
Locker
에 있는 경우:Member
를 조회할 때 항상Locker
테이블을 추가로 조회해야 하므로, 지연 로딩 설정이 무시되고 즉시 로딩이 이루어집니다. - 외래키가
Member
에 있는 경우:Member
테이블만으로Locker
와의 연결 여부를 확인할 수 있으므로, 지연 로딩과 즉시 로딩 설정이 가능합니다.
'Spring' 카테고리의 다른 글
JPQL 실행 시 Flush와 영속성 컨텍스트 동작 확인 (0) | 2024.07.22 |
---|---|
API 통신 시 null 값 처리: 포함 vs. 미포함의 장단점 (0) | 2024.07.22 |
[Spring] DynamicInsert 사용 이유 (0) | 2024.07.22 |
Optional 클래스의 orElseThrow (0) | 2024.07.22 |
[Spring] Enum 타입으로 Exception 구현하기 (0) | 2024.07.22 |
[Spring] DynamicInsert 사용 이유
[Spring] @DynamicInsert 사용이유
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Knowledge {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String thumbnailImageUrl;
@Column(nullable = false)
private String contentImageUrl;
@Column(columnDefinition = "boolean default false")
private Boolean isDeleted;
@Column(updatable = false)
@CreationTimestamp
private LocalDateTime createdAt;
}
위와 같은 knowledge 객체를 생성 및 저장 할 때 isDeleted
필드 값을 설정하지 않으면, 데이터베이스에 null
값이 저장됩니다. 이는 데이터베이스에 기본값이 true
또는 false
로 설정되어 있더라도 발생할 수 있는 문제입니다.
예를 들어, Knowledge
Entity 가 위와 같을때 Knowledge 객체를 저장하게 되면(isDeleted 가 null 인 상태로), 아래와 같은 쿼리가 생성됩니다:
2024-01-03T16:21:44.759+09:00 DEBUG 32029 --- [ main] org.hibernate.SQL :
insert
into
knowledge
(thumbnail_image_url, contentImageUrl, isDeleted, createdAt)
values
(?, ?, ?, ?)
이 쿼리는 isDeleted
필드가 명시적으로 null
로 설정되었기 때문에, 데이터베이스에 false
가 아닌 null
이 저장되는 문제를 발생시킵니다.
이를 해결하기 위해 @DynamicInsert
어노테이션을 사용할 수 있습니다. @DynamicInsert
를 사용하면 isDeleted
필드가 null
일 경우 INSERT
문에서 제외됩니다. 따라서 데이터베이스는 isDeleted
필드에 대한 기본값을 적용하여 false
를 저장하게 됩니다.
2024-01-03T16:21:44.759+09:00 DEBUG 32029 --- [ main] org.hibernate.SQL :
insert
into
knowledge
(thumbnail_image_url, content_image_url, created_at)
values
(?, ?, ?)
@DynamicInsert
어노테이션을 사용하면, 엔티티 객체의 null
값을 가진 필드를 INSERT
문에서 제외하여 데이터베이스의 기본값이 적용되도록 할 수 있습니다. 이를 통해 Knowledge
엔티티 클래스의 isDeleted
필드가 올바르게 처리되며, 불필요한 null
값 저장을 방지할 수 있습니다.
'Spring' 카테고리의 다른 글
API 통신 시 null 값 처리: 포함 vs. 미포함의 장단점 (0) | 2024.07.22 |
---|---|
JPA에서 단방향 및 양방향 일대일 관계의 외래키 처리와 지연 로딩 문제 (1) | 2024.07.22 |
Optional 클래스의 orElseThrow (0) | 2024.07.22 |
[Spring] Enum 타입으로 Exception 구현하기 (0) | 2024.07.22 |
docker hub 사용하는 방법 (1) | 2023.11.27 |
Optional 클래스의 orElseThrow
Optional 클래스의 orElseThrow
orElseThrow
는 Optional
클래스의 메서드로, 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 예외를 던지도록 설계되었습니다. 이 메서드는 Supplier
인터페이스를 통해 예외를 생성하는데, Supplier
는 인자를 받지 않고 결과를 반환하는 함수형 인터페이스입니다.
orElseThrow
메서드는 다음과 같이 정의되어 있습니다
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
이 메서드는 값이 존재하면 그 값을 반환하고, 값이 없으면 Supplier
를 통해 예외를 생성하여 던집니다. Supplier
는 @FunctionalInterface
어노테이션이 붙은 인터페이스로, 단 하나의 추상 메서드 get
을 가지고 있습니다
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
`
orElseThrow
메서드를 사용할 때는 보통 람다 표현식을 사용하여 예외 객체를 생성합니다. 예를 들어, find
메서드에서 특정 ID에 해당하는 Knowledge
객체를 찾지 못했을 때 예외를 던지도록 다음과 같이 작성할 수 있습니다:
public Knowledge find(Long knowledgeId) {
return knowledgeRepository.findById(knowledgeId).orElseThrow(() -> new CustomRuntimeException(
KnowledgeException.KNOWLEDGE_NOT_FOUND));
}
이 코드는 knowledgeId
에 해당하는 Knowledge
객체가 존재하지 않으면 CustomRuntimeException
을 던집니다. 여기서 사용된 람다 표현식 () -> new CustomRuntimeException(KnowledgeException.KNOWLEDGE_NOT_FOUND)
는 Supplier<CustomRuntimeException>
타입의 인스턴스를 생성합니다. (CustomRuntimeException 구조)
최종적으로 위와같이
public Knowledge find(Long knowledgeId) {
return knowledgeRepository.findById(knowledgeId).orElseThrow(() -> new CustomRuntimeException(
KnowledgeException.KNOWLEDGE_NOT_FOUND));
}
예외가 던져진다하면 orElseThrow
안에 제네릭은 타입 추론에 의하여
public <CustomRuntimeException extends Throwable> T orElseThrow(Supplier<? extends CustomRuntimeException> exceptionSupplier) throws CustomRuntimeException {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
이렇게 바뀌고 value
즉 위에선 Knowledge
가 없는경우 CustomRuntimeException
이 throw
되게 됩니다.
그후 ControllerAdvice
등의 예외 처리 로직에 의하여 예외가 처리 됩니다.
'Spring' 카테고리의 다른 글
JPA에서 단방향 및 양방향 일대일 관계의 외래키 처리와 지연 로딩 문제 (1) | 2024.07.22 |
---|---|
[Spring] DynamicInsert 사용 이유 (0) | 2024.07.22 |
[Spring] Enum 타입으로 Exception 구현하기 (0) | 2024.07.22 |
docker hub 사용하는 방법 (1) | 2023.11.27 |
redis를 통해 로그아웃 기능 구현 (0) | 2023.11.27 |
[Spring] Enum 타입으로 Exception 구현하기
[Spring] Enum 타입으로 Exception 구현하기
기존에 예외 처리를 다음과 같이 Enum
타입으로 구현했습니다.
public enum AdminException {
FAIL_TO_SIGN_IN(HttpStatus.BAD_REQUEST, "로그인에 실패했습니다.");
private final HttpStatus status;
private final String message;
}
Enum
타입은 기본적으로 java.lang.Enum
을 암시적으로 상속받기 때문에 extends
키워드를 사용할 수 없습니다. 따라서 상속 기능을 구현하기 위해 interface
를 사용했으며, interface
는 다중 상속을 지원하므로 아래 코드와 같이 enum
클래스들이 CustomException
인터페이스를 implements
하도록 했습니다.
public interface CustomException {
HttpStatus getHttpStatus();
String getErrorMessage();
String getName();
}
@RequiredArgsConstructor
public enum AdminException implements CustomException {
FAIL_TO_SIGN_IN(HttpStatus.BAD_REQUEST, "로그인에 실패했습니다.");
private final HttpStatus status;
private final String message;
@Override
public HttpStatus getHttpStatus() {
return status;
}
@Override
public String getErrorMessage() {
return message;
}
@Override
public String getName() {
return name();
}
}
Enum
타입이 특정 인터페이스를 implements
한 이유는 RuntimeException
을 상속받는 특정 클래스(아래에서는 CustomRumtimeException
)에서 모든 예외에 대해 동일한 응답을 제공하기 위해서였습니다. 해당 클래스는 다음과 같습니다.
@Getter
@RequiredArgsConstructor
public class CustomRuntimeException extends RuntimeException {
private final CustomException customException;
public String getMessage() {
return customException.getErrorMessage();
}
}
위 코드는 CustomRuntimeException
클래스가 CustomException
인터페이스를 구현하는 열거형을 사용하여 예외 정보를 처리하고, 예외 발생 시 일관된 응답을 반환하도록 설계되었습니다. 이를 통해 각 예외에 대한 HTTP 상태 코드와 메시지를 통일된 형식으로 관리할 수 있습니다.
최종적으로 CustomRuntimeException
을 ControllerAdvice
또는 RestControllerAdvice
에서 아래와 같이 예외를 잡고 응답을 보낼 수 있습니다.
@ExceptionHandler(CustomRuntimeException.class)
public ResponseEntity<Map<String, String>> customExceptionHandler(CustomRuntimeException e) {
// 응답 맵 생성
Map<String, String> responseMap = new LinkedHashMap<>();
responseMap.put("status", e.getCustomException().getHttpStatus().toString());
responseMap.put("message", e.getCustomException().getErrorMessage());
// 응답 반환
return ResponseEntity.status(e.getCustomException().getHttpStatus())
.body(responseMap);
}
이렇게 하면 예외 발생 시 CustomRuntimeException
을 던지고, GlobalExceptionHandler
가 해당 예외를 잡아 일관된 형식으로 응답을 반환하게 됩니다.
'Spring' 카테고리의 다른 글
[Spring] DynamicInsert 사용 이유 (0) | 2024.07.22 |
---|---|
Optional 클래스의 orElseThrow (0) | 2024.07.22 |
docker hub 사용하는 방법 (1) | 2023.11.27 |
redis를 통해 로그아웃 기능 구현 (0) | 2023.11.27 |
Jasypt 를 통한 암호화 (2) | 2023.11.27 |
ElasticSearch 를 사용하기 위해 SSL/TLS인증서 추가방법
SSL/TLS 인증 추가하기
https
를 사용하는 ElasticSearch 를 사용해 데이터를 조회하려 했는데
unable to find valid certification path to requested target
와 같은 오류가 발생했다.
해당오류는 SSL
인증서가 없기 때문에 발생한 오류다. 따라서 SSL
인증서를 추가해야하며 방법은 아래와 같다.
1. cacert 에 import 하기
기본적으로 SSL
인증서를 등록하기 위해선 당연히 SSL
인증서가 필요하다.
docker
를 통해 ElasticSearch
를 실행했다면, 아래와 같은 명령어로 인증서를 복사할수 있다.
docker cp [ElasticContainerID]:/usr/share/elasticsearch/config/certs/http_ca.crt .
위 명령어를 통해 http_ca.crt
인증서를 복사했다면 해당 인증서를 JAVA
의 keystore
에 등록해줘야한다.
keystore
에 등록하는 방법은 cacerts
라는 파일에 import
를 해주면 된다.
[sudo] keytool -import -alias [별칭이름] -keystore [cacerts 경로] -file [http_ca.crt 경로]
나같은 경우에 cacerts
의 경로는 아래와 같았다.
/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/lib/security/cacerts
참고로 권한이 없다고하면 sudo
를 붙여 명령어를 실행해주자.
cacerts
에 import
를 하기 위해선 비밀번호가 필요한데 바꾸지 않았다면 changeit
이다.
cacerts
파일에 ssl
인증서를 import
했다면, 이젠 intellij 에서 vm
옵션을 통해 아래 명령어를 추가해주면 된다.
2. Intellij 의 VM 옵션 추가하기
-Djavax.net.ssl.trustStore=[cacerts 경로] -Djavax.net.ssl.trustStorePassword=[cacerts 비밀번호]
이제 다시 실행해보면 https
를 사용하는 elasticSearch
로부터 정상적인 응답을 받는것을 확인할수 있다.
'Java' 카테고리의 다른 글
[Java] OOP 4대원칙 및 SOLID원칙 (0) | 2023.04.09 |
---|
docker hub 사용하는 방법
1. docker 홈페이지 가서 아이디 만들기
내 프로필은 charliekorean
이다.
2. local 에서 docker에 로그인하기
docker login
위 명령어를 통해 docker에 login 할수있다. docker의 아이디 및 비밀번호를 입력하면 된다.
3. docker hub에 image push 하기
현재 docker에 존재하는 모든 이미지중 mysql:latest
를 테스트삼아 docker hub에 올려보겠다.
우리가 docker hub에 이미지를 올리게되면 repository가 생성이 되어 그안에 우리가 push한 이미지가 존재하게 된다. 이때 repository명은 항상 고유해야한다. 따라서 {username}/{repository}:{tag} 형식으로 위 이미지 명을 바꿔줘야한다.
docker tag mysql:latest charliekorean/mysql:latest
위 명령어를 입력후 image를 출력해보면 같은 image ID에 대한 서로다른 repository 명을 가진 image가 생성된것을 알수있다.
이후 이 이미지를 docker hub로 push하면 된다.
docker push charliekorean/mysql:latest
4. docker hub에 올라간 이미지 확인
5. docker hub에 올린 이미지 pull 받기
docker pull charliekorean/mysql:latest
간단하다 우리가 올린 image를 그대로 pull 해주고 image를 확인해보면 잘 다운받아 진것을 알수있다.
'Spring' 카테고리의 다른 글
Optional 클래스의 orElseThrow (0) | 2024.07.22 |
---|---|
[Spring] Enum 타입으로 Exception 구현하기 (0) | 2024.07.22 |
redis를 통해 로그아웃 기능 구현 (0) | 2023.11.27 |
Jasypt 를 통한 암호화 (2) | 2023.11.27 |
checkedException, uncheckedException (1) | 2023.11.22 |