Server/Spring JPA 26

Spring - @Transactional(readOnly = true)을 하면 일어나는 일

왜 Service 클래스 단위에 기본적으로 @Transactional(readOnly = true) 속성을 사용할까? @Trasnactional(readOnly = true)를 사용하면 어떤 점에서 이득이 있는 지 알아보자. 예상치 못한 엔터티의 등록, 변경, 삭제 예방 @Transactional에 readOnly = true 옵션을 주면 스프링 프레임워크가 세션 플러시 모드를 MANUAL로 설정한다. 이렇게 하면 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않게 되어서, 트랜잭션이 커밋되면서 실수로 엔터티가 등록, 수정, 삭제 되는 일을 방지한다. 성능 최적화 1. 트랜잭션을 읽기 전용으로 열면 JPA에서 제공하는 스냅샷 저장이나 변경 감지 등을 사용하지 않아서 자원(CPU, Memory)의 낭..

Server/Spring JPA 2022.11.04

Spring - Service Layer에서 Storage를 다룰 때 트랜잭션 처리하기! (커넥션, 락 과점유 피하기!)

DB 자원을 등록할 때 파일을 추가로 업로드하는 경우가 있다. 조회를 할 때 파일도 추가로 조회해야 하는 경우도 있다. Service에서 스토리지 다루기 아래 예시를 보자. @Service @RequiredArgsConstructor public class MemberService { private final MemberRepository memberRepository; private final MemberResourceService resourceService; @Transactional public Member create(CreateMemberRequest request) { Member member = new Member(request.getUsername()); memberRepository.s..

Server/Spring JPA 2022.10.25

Spring Transaction - Propagation(전파 속성)

Transaction Propagation Spring Transaction의 Propagation(전파 범위)에 대해서 알아보자. 트랜잭션 전파 범위는 트랜잭션을 시작하거나 기존 트랜잭션에 참여하는 방법과 롤백되는 범위 등에 대한 속성값이다. 종류는 다음의 7가지가 있다. REQUIRED(default) REQUIRES_NEW MANDATORY SUPPORTS NESTED NEVER REQUIRED default 속성이다. Required는 부모 트랜잭션이 존재한다면 부모 트랜잭션에 합류한다. 그렇지 않다면 새로운 트랜잭션을 만든다. 중간에 자식 / 부모에서 예외가 발생한다면 자식과 부모 모두 rollback 한다. REQUIRES_NEW 무조건 새로운 트랜잭션을 만든다. nested한 방식으로 메소드..

Server/Spring JPA 2022.10.04

JPA - Update(수정) 시 save() 메서드를 호출하는 것이 좋을까?

JPA를 사용하면 트랜잭션 범위 안에서 Dirty Checking이 동작한다. 따라서 save() 메서드를 호출하지 않아도 값이 알아서 수정되고 반영된다. 그렇다면 save()를 호출하는 것이랑 어떤 차이가 있는 지 알아보자. 차이 먼저 @Transactional만을 사용한 예제를 보자. @Transactional public Notice update(Long noticeId, String content) { Notice notice = noticeRepository.findById(noticeId).get(); notice.setContent(content); } 다음은 repository.save() 메서드를 사용한 예제를 보자. public Notice update(Long noticeId, Str..

Server/Spring JPA 2022.09.15

Spring - JpaRepository가 아닌 Repository를 사용해야 하는 이유!

Spring Data JPA를 사용하면 일반적으로 Repository에서 다음의 인터페이스 중 하나를 상속하여 사용하게 된다. JpaRepository CrudRepository Repository 해당 인터페이스들은 어떤 부분이 다르며 Repository를 사용하는 것을 권장하는 이유가 무엇인지 알아보자. JpaRepository 아래의 그림은 Spring Data Jpa에서 제공하는 인터페이스 사이의 상속관계를 정리한 것이다. 그림에서 아래로 갈 수록 저수준 모듈이며 기능 구현이 많음을 알 수 있다. CrudRepository의 경우 메서드를 정의하지 않아도 간단한 CRUD 사용이 가능하다. public interface AccountRepository extends CrudRepository { }..

Server/Spring JPA 2022.07.18

Spring Boot - 커넥션 풀 상태 확인하기!

Spring Boot 2.0 이상에서는 DBCP(DataBase Connection Pool) 구현체를 default로 HikariCP를 사용하고 있다. 다음의 HikariCP의 프로퍼티를 수정해서 현재 커넥션풀의 상태를 확인할 수 있다. application.properties logging.level.com.zaxxer.hikari=TRACE logging.level.com.zaxxer.hikari.HikariConfig=DEBUG application.yml logging: level: com.zaxxer.hikari: TRACE com.zaxxer.hikari.HikariConfig: DEBUG 결과 아래와 같은 로그를 확인하는 것이 가능하다. 예제 앱에서는 데이터 소스를 4개 사용하고 있다. 하..

Server/Spring JPA 2022.06.11

QueryDSL - Select 성능 개선하는 방법들! (+ JPQL 관련 이슈)

해당 포스팅은 "[우아콘2020] 수십억건에서 QUERYDSL 사용하기" 라는 영상의 내용을 정리한 글입니다. 해당 영상은 아래 Reference에서 시청할 수 있습니다. - https://www.youtube.com/watch?v=zMAX7g6rO_Y exist 메소드 금지 SQL문 중 exists 문은 조건을 만족하는 레코드를 1개 찾으면 바로 결과를 반환하고 쿼리를 종료한다. 반면 count(1)의 경우 마지막 레코드까지 검사를 하게 되기 때문에 성능이 낭비된다. 이런 성능차이는 스캔 대상이 앞에 위치할수록 더 심하게 발생한다. 문제는 QueryDSL의 exists는 SQL문의 exists를 사용하지 않고 count()>0로 쿼리를 날린다. 이를 개선하기 위해서 Querydsl의 selectOne..

Server/Spring JPA 2022.05.29

Java - LazyConnectionDataSourceProxy란? (+Dynamic DataSource)

LazyConnectionDataSourceProxy Spring은 트랜잭션에 진입하면 즉시 미리 DataSource의 커넥션을 가져온다. 가령 ServiceLayer Method에 @Transactional이 걸려있다고 가정하자. 쿼리를 날리기 전에 여러가지 복잡한 작업이 있을 수 있다. 그런데 Spring에서는 @Transactional에 진입한 순간 바로 커넥션을 걸어버린다. 이런 방식은 여러 단점이 있다. Ehcache같은 캐시를 사용하는 경우 실제 DB에 접근하지 않지만 불필요한 커넥션을 점유 JPA의 영속성 컨텍스트에서 엔터티가 존재함에도 불필요한 커넥션을 점유 Dynamic DataSource 환경에서 DataSource도 정해지지 않았는데 트랜잭션에 진입하면서 에러 터짐 커넥션 점유시간이 ..

Server/Spring JPA 2022.05.21

QueryDSL - 벌크(Bulk) 연산 시 주의할 점!

벌크(Bulk) 연산 주의사항 Querydsl에서 Bulk 연산을 할 때 주의사항이 있다. 예를 통해 살펴보자. 아래 메서드는 모든 member의 money를 0으로 초기화한다. public void bulkUpdate() { // init Money queryFactory .update(member) .set(member.money, 0) .execute(); } 문제는 이때 쿼리를 날려서 DB에 반영은 하지만, 영속성 컨텍스트에 반영하지 않는다. 문제 상황 기존에 아래와 같이 Member가 3개가 있다고 가정하자. - id: 1, money: 10000 - id: 2, money: 20000 - id: 3, money: 30000 해당 Member들이 영속성 컨텍스트에 들어 있을 때 Bulk 연산을 ..

Server/Spring JPA 2022.05.08

Querydsl - 동적 쿼리(Dynamic SQL) 사용하기 !

동적 쿼리란 ? 동적 쿼리란 상황에 따라 다른 문법의 SQL을 적용하는 것을 말한다. 예를 들면 DB에서 값을 조회할 때 조회 조건이 동적으로 바뀌어야 하는 경우가 많다. 이런 상황을 Querydsl을 사용하면 손쉽게 해결할 수 있다. name 값이 들어오면 WHERE name = ${name} age 값이 들어오면 WHERE age = ${age} name과 age가 모두 들어오면 WHERE name = ${name} AND age=${age} name과 age 모두 들어오지 않으면 WHERE 절을 사용하지 않는다. 이를 해결하기 위한 방법을 살펴보자. 1. BooleanBuilder 동적 쿼리를 해결하려고 BooleanBuilder를 사용하는 걸 자주 볼 수 있다. private List search..

Server/Spring JPA 2022.05.07