자바 ORM 표준 JPA 프로그래밍, 김영한
모르는 프로그래밍 개념과
궁금한 부분 추가 학습
3 영속성 관리
detached 상태의 엔티티는 더이상 영속성 컨텍스트에 존재하지는 않지만, heap에 객체로 남아 있다.
detached 상태의 엔티티는 merge()로 다시 영속성 컨텍스트에 병합되어 관리될 수 있다. (재사용 가능)
Removed: 메모리에 남아 있을 수 있지만, 더 이상 유효하지 않으며, 곧 데이터베이스에서 삭제될 예정. 더 이상 사용되지 않아야 함.
When do entities get detached and removed?
Detached: 트랜잭션 종료, 명시적 detach() 호출, 영속성 컨텍스트 종료, 서비스 메서드 종료 시 발생.
Removed: remove() 메서드를 호출할 때 발생.
=> 대부분의 경우 그냥 detached임
12.5 명세
명세SPECIFICATION
DDD(Domain Driven Design) 책에서 소개한 개념
스프링 데이터 JPA : JPA Criteria로 해당 개념을 사용 - 술어를 Specification 클래스로 정의 (예를 들면 데이터 검색을 위한 제약 조건 하나하나)
술어predicate
Predicate Logic 술어 논리학
술어 (Predicate)
ㅇ 변수가 포함된 문장(명제)
ㅇ 특징
- 변수가 특정값으로 정해지면 술어는 명제가 됨
. 즉, 변수가 정해지면 답을 내놓게 됨
- 통상, 연산기호(+ - x / < > 등)와 변수들을 함께 써서 표현 가능
※ [참고]
- 프로그래밍 언어 상의 술어는
. 반환값으로 진리값(참/거짓)을 주는 함수를 주로 가리킴
- 자연 언어 상의 술어는
. 주어(명사) + 술어(동사,명사) 처럼, 주어에 대해 주장(긍정,부정)하는 역할을 갖는,
. 문장 구성 요소 중 하나를 가리킴
http://www.ktword.co.kr/test/view/view.php?no=1022
술어 함수(Predicate Function)
술어 함수는 문법 규칙과 연관되며 모든 술어 함수는 논리형(boolean)으로 정의되어야 하고, 반드시 참이여야만 합니다.
명세 스펙 Specification
일반적으로는 기능 요구 사항이나 기술적인 상세 설명
문맥에 따라 다르지만, 기본적으로 "어떻게 동작해야 한다"는 것을 정의하는 데 사용된다.
Domain-Driven Design(DDD)에서 말하는 Specification은 비즈니스 규칙을 정의하고 이를 검증하는 패턴으로 사용.
- 조합 가능한 규칙 : 여러 규칙을 논리적으로 조합할 수 있다. 여러 개의 Specification을 AND, OR, NOT과 같은 논리 연산을 통해 결합하여 복잡한 조건을 만들 수 있다.
- 복잡한 비즈니스 규칙을 명확, 단순, 직관적으로 표현 --> 도메인 객체가 특정 조건을 충족하는지 검증
12.7.2
JPA 도메인 클래스 컨버터
참고)
클라이언트가 HTTP요청을 보내면 Spring MVC가 해당 요청을 수신하고, 핸들러 매핑을 한다. 핸들러 어댑터는 매핑된 핸들러(컨트롤러)를 호출하는 과정에서 다음과 같은 동작을 수행한다.
Message Converter가 content-type을 확인하고 적절한 변환기를 선택하여 데이터를 처리한다
도메인 클래스 컨버터는 데이터베이스의 값과 엔티티 필드 간 변환이 필요할 때 쓰임.
책에 나온 예시의 경우, URL에서 전달된 id로 Member 객체를 조회하고 뷰에 전달하는 경우는 단순 조회로, 도메인 클래스 컨버터가 필요하지 않음.
@DomainClassConverter는 Spring Data JPA에서 제공하는 기능으로, URL로 전달된 식별자(id)를 데이터베이스에서 해당 엔티티 객체로 변환해주는 역할을 한다. 이렇게 하면, 컨트롤러에서 id를 따로 조회할 필요 없이 해당 엔티티를 바로 사용할 수 있다.
하지만, 도메인 클래스 컨버터는 특정 필드의 타입이 데이터베이스와 직접적인 변환이 필요할 때 사용된다. 예를 들어, 데이터베이스에 저장되는 값이 Member 객체로 바로 매핑되지 않고, 특별한 변환 로직을 적용해야 한다면 @Convert를 사용해야 할 수 있다. 하지만, id=1과 같은 요청 파라미터를 받아서 그 값으로 Member 객체를 조회하고, 그 객체를 모델에 담아 뷰로 전달하는 과정은 단순히 데이터베이스 조회에 불과하므로 도메인 클래스 컨버터가 필요하지 않다.
12.9.3
static, final, inner class에 대해 헷갈리는 지점들 정리

static 메서드는 클래스가 로드되는 시점에 등록 됨. 인스턴스 메서드가 아닌 클래스 메서드이기 때문에 객체 생성을 하지 않고 클래스 이름으로 호출 가능. OrderSpec.memberNameLike()
final 매개변수를 사용하면 값의 불변성이 보장된다
왜 불변하게 하지?
=> 코드 가독성 향상 : 코드를 읽을 때 다른 값이 대입될 거란 걱정을 하지 않아도 됨
=> 안정성 높임, 디버깅 쉬움 : 의도치 않게 변경되지 않음
=> 멀티스레드 환경에서 안정성 제공 : 다른 스레드에 의해 변경될 수 없기 때문에 동시성 문제를 예방. , 특히 매개변수가 참조 타입일 경우 이를 사용하면 안전한 참조를 보장할 수 있다.
inner class는 outer class의 멤버처럼 동작한다. 즉 outer class의 인스턴스에 종속된다.
static inner class는 outer class의 인스턴스에 종속되지 않고 독립적으로 존재할 수 있기 때문에, static inner class에서만 static method 선언이 가능하다
static import
static method를 import하면 클래스명 없이 해당 메소드를 바로 호출할 수 있다.
주의:
코드 가독성을 떨어뜨릴 수 있으므로, 적절히 사용.
같은 이름의 static 메소드가 다른 클래스에 여러 개 있을 경우, 혼란을 초래할 수 있다.
12.10.2 QueryDslRepositorySupport 클래스
public class OrderRepositoryImpl extends QueryDslRepositorySupport implements CustomOrderRepository
의 생성자에서 super() 매개변수로 해당 리포지토리에서 다룰 도메인 클래스를 명시
부모 클래스인 QueryDslRepositorySupport 의 생성자를 호출하면서
QueryDslRepositorySupport에게 연결되어야 할 엔티티 클래스를 알려줘서
QueryDsl을 사용하도록 설정
13.3.3
OSIV의 한계
같은 JVM을 벗어난 원격 상황에서는 사용할 수 없다.
OSIV가 세션을 열어두는 개념은 같은 서버 내부에서만 의미가 있다. 클라이언트가 서버의 내부 동작(세션 유지, 엔티티 관리 등)을 제어할 수 없다는 사실. 두 시스템이 별도의 환경에서 동작하기 때문
=> 너무 당연한데 왜 언급?
OSIV의 사용 시점과 한계를 명확히 이해하기 위해.
단일 JVM 내에서 실행되는 웹 애플리케이션에서는 OSIV를 사용할 수 있다. 예를 들면 서버 사이드 렌더링, 내부 서비스 간 호출의 경우.
그러나 REST API 요청으로 데이터를 주고받는 경우, 서버에서 데이터를 직렬화해 JSON으로 변환하여 클라이언트에 보내기 때문에, 이 시점에 지연 로딩이 필요하면 이미 Hibernate 세션이 닫혀 있어서 문제가 발생. 흔히 LazyInitializationException이 발생.
=> 그렇다면 MSA 환경에서는 어떨까?
각 마이크로서비스가 독립적인 애플리케이션으로 운영되고, 서로 원격 API 호출로 통신하기 때문에, OSIV의 지연 로딩 특성을 활용하는 것이 불가능하거나 권장되지 않음! => 각 마이크로서비스 간에 명시적으로 데이터를 로드하여 전달하는 더 안정적인 방식이 필요. DTO패턴 즉시 로딩(eager loading), GraphQL( 더 정교한 데이터 처리 방법 ), API Gateway가 사용될 수 있다.