[Spring JPA] @Transactional 전파 속성
·
Spring
최근 부모 메서드 → 자식 메서드 호출 흐름에서 트랜잭션이 어떻게 이어지는지 혼동이 있었다. 이 글은 저장과 롤백 결과 중심으로, 부모/자식 메서드 4가지 조합을 간단한 코드로 비교한다.1. @Transactional과 AOP Proxy의 상관관계@Transactional은 Spring AOP 프록시로 동작한다. 프록시가 메서드 호출 앞뒤로 트랜잭션 시작/커밋/롤백을 감싼다.같은 클래스 내부에서 **자기 자신 메서드 호출(self-invocation)**을 하면 프록시를 우회해 어노테이션이 적용되지 않는다.안전한 패턴은 다음 둘 중 하나다.부모/자식을 서로 다른 빈으로 분리하여 호출이 프록시를 통과하도록 구성필요 시 ApplicationContext에서 자기 자신의 프록시 빈을 주입받아 그 빈으로 호출핵..
[Spring] QueryStringArgumentResolver으로 쿼리 파라미터 객체 바인딩하기
·
Spring
기본적으로 Spring MVC에서는 GET 방식의 쿼리 파라미터를 단일 값으로 바인딩한다.하지만 구조가 있는 객체로 받고 싶은 경우, @RequestParam이나 @ModelAttribute만으로는 부족하다.이 글에서는 커스텀 어노테이션과 HandlerMethodArgumentResolver를 이용해GET 쿼리 파라미터를 하나의 객체로 바인딩하는 방법을 정리한다.문제 확장: 객체 단위로 받기다음과 같은 요청을 생각해보자.GET /api/search?keyword=apple&status=NEW&status=HOLDkeyword는 단일 값status는 복수 값 (리스트)이처럼 하나의 요청에서 단일 값과 리스트를 동시에 받아야 할 때,단순 파라미터 바인딩만으로는 깔끔하게 처리하기 어렵다.접근 방식 – 커스텀 ..
[Spring JPA] AttributeConverter를 사용하여 Entity 필드 타입 변환하기
·
Spring
Entity의 필드 타입과 실제 DB에 저장되는 데이터 타입이 다를 때,JPA에서는 @Convert 어노테이션과 AttributeConverter 인터페이스를 사용해 이를 해결할 수 있다.문제 확장: 서로 다른 형식 간 변환예를 들어 Entity에서는 Boolean 타입을 쓰고 싶지만,DB에는 "Y" / "N" 문자열로 저장해야 하는 경우가 있다.이처럼 서로 다른 타입 간 변환이 필요한 상황에서는 Converter 클래스를 정의하면 된다.접근 방식 – AttributeConverter 구현핵심은 다음 두 메서드를 오버라이드하는 것이다.convertToDatabaseColumn: Entity의 값을 DB 저장용 값으로 변환convertToEntityAttribute: DB에서 가져온 값을 Entity 타입..
[Spring JPA] 지연 로딩과 1+N 문제 해결 전략
·
Spring
JPA를 사용할 때 가장 자주 마주치는 문제 중 하나가 바로 1+N 문제다.연관된 엔티티가 지연 로딩(LAZY)으로 설정돼 있을 때 루프 안에서 매번 추가 쿼리가 발생하면서 전체 쿼리 수가 급격히 늘어나는 현상이다.문제 정의: 1+N이란?예를 들어 N개의 부모 엔티티를 조회하고, 각 부모마다 자식 엔티티를 조회한다고 하자.이때 다음과 같은 쿼리가 실행된다.부모 조회: 1번자식 조회: N번→ 총 1 + N 쿼리로딩 전략: LAZY vs EAGERJPA에서 연관 관계는 두 가지 방식으로 로딩된다. 로딩 방식설명권장 여부LAZY (지연 로딩)실제 사용할 때 쿼리를 실행✅ 권장EAGER (즉시 로딩)엔티티를 조회할 때 연관 엔티티도 함께 로딩❌ 지양 즉시 로딩은 예측 불가능한 쿼리를 발생시키고 성능 저하의 원인..
[Spring QueryDsl] DTO 매핑을 위한 Projection 사용 방법
·
Spring
QueryDSL에서는 Entity가 아닌 DTO로 직접 결과를 매핑할 수 있도록 여러 가지 Projection 방식을 제공한다.이 글에서는 그 중 자주 쓰는 세 가지 방식과 각각의 특징을 정리한다.1. Projections.bean (Setter 방식)Setter 메서드를 통해 값을 주입한다.DTO에 기본 생성자와 public setter가 있어야 한다.public void findDtoBySetter() { List members = queryFactory .select(Projections.bean(MemberDto.class, member.name, member.age )) .from(member) .fe..
[Spring QueryDsl] BooleanBuilder로 동적 조건 구성하기
·
Spring
QueryDSL을 사용할 때, 검색 조건이 유동적으로 바뀌는 상황에서는 조건문을 동적으로 구성할 수 있어야 한다.이때 사용하는 도구가 BooleanBuilder다.BooleanBuilder 기본 구조BooleanBuilder builder = new BooleanBuilder();builder.and(조건);builder.or(다른조건);조건을 하나씩 누적해서 붙일 수 있고,필요 없는 조건은 넣지 않으면 된다.BooleanExpression vs PredicateQueryDSL 조건은 두 가지로 구성된다.BooleanExpression: and, or 조합이 가능함 (주로 사용)Predicate: 단순 조건 표현, 조합이 어려움따라서 실무에서는 대부분 BooleanExpression 기반으로 메서드를 만..
[Spring QueryDsl] 벌크 연산(Bulk Operation) 사용 시 주의점
·
Spring
JPA에서는 대량 데이터를 한 번에 수정하거나 삭제할 수 있도록 벌크 연산(Bulk Operation) 기능을 제공한다.QueryDSL이나 JPQL을 사용할 때 .execute()로 실행된다.기본 사용 방식UPDATE 예시queryFactory .update(member) .set(member.money, 0) .execute();member 테이블의 모든 money 값을 0으로 일괄 수정한다.조건이 없으므로 전체 레코드가 대상이다.DELETE / INSERT 예시queryFactory.delete(member) .where(member.age.gt(60)) .execute();queryFactory.insert(member) .columns(member.name, mem..
[Spring QueryDsl] Sub Query는 가급적 조인으로 대체하거나 쿼리 분할로 해결하자
·
Spring
QueryDSL에서도 서브쿼리(SubQuery)를 사용할 수 있다.하지만 복잡한 쿼리나 데이터 양이 많아질수록 성능 이슈가 발생할 수 있으므로, 가능하다면 조인 방식으로 대체하거나 쿼리를 분리해서 처리하는 것이 더 안전하다. 특히 IN 서브쿼리는 최적화가 어렵고, 쿼리 성능이 급격히 떨어질 수 있다.1. SELECT 절 – 서브쿼리로 컬럼 집계특정 컬럼 값에 대해 서브쿼리를 함께 조회할 수 있다.아래는 학원 이름과 해당 학원 소속 학생 수를 함께 조회하는 예제다.public List findAllStudentCount() { return queryFactory .select(Projections.fields(StudentCount.class, academy.name...