스프링 JPA

__〆( ̄ー ̄ ) ·2025년 2월 11일

스프링 부트

목록 보기
9/9

스프링 JPA란

스프링에서 데이터를 다루는 여러 방법 (JDBC, MyBatis, MongoDB 등) 중 하나로 스프링 JPA (Java Persistence API)는 자바 ORM (Object-Relational Model) 표준 기술인 JPA를 스프링에서 쉽게 사용할 수 있게 해요.

JPA란?
JPA는 자바에서 관계형 (Relational) 데이터베이스를 사용하는 방식을 API화 한 것으로 직접적인 SQL 없이도 객체를 통해 데이터를 다룰 수 있어요.

먼저 JPA의 핵심 기능인 엔티티 매핑Repository 인터페이스를 설명드릴게요.

엔티티 매핑

JPA에서는 먼저 @Entity 어노테이션을 통해 엔티티를 정의해요. 여기서 엔티티는 관계형 데이터베이스에서 하나의 테이블을 의미해요. 예시를 볼게요.

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    // ...
}

이 코드는 idname 칼럼을 가진 Member 테이블을 정의하고 있어요. 그리고 이 클래스의 인스턴스 하나 하나는 테이블의 열(row)에 해당합니다.

JPA는 이와 같이 엔티티 클래스를 통해 데이터를 객체 중심으로 처리할 수 있게 해요.

Repository 인터페이스

Repository 인터페이스는 데이터베이스 연산을 처리하는 기능을 제공해요. 데이터베이스 연산을 위한 인터페이스를 정의하면 JPA에서 자동으로 인터페이스 구현체를 생성해요.

기본적으로 추가적인 인터페이스를 정의하지 않아도 CRUD (Create, Read, Update, Delete) 기능을 제공합니다.

public interface MemberRepository extends JpaRepository<Member, Long> {
    // ...
}

해당 MemberRepository는 아래와 같이 으로 주입받아 사용 가능해요.

@Service
@RequiredArgsConstructor
public class MemberService {
    private final MemberRepository memberRepository;
    
    public void createUser(User user) {
    	memberRepository.save(user);
    }
    
    public User getUser(long id) {
    	return memberRepository.findById(id);
    }
    
    public void deleteUser(User user) {
    	return memberRepository.delete(user);
    }    
    // ...    
}

위 예시에서 save, findById, delete가 기본적으로 제공되는 JPA API 들이에요. save는 DB insertupdate를 겸하는데 이미 해당 데이터가 있다면 update 처리해요.

이름으로 사용자 목록을 조회하는 API가 필요하다면 아래와 같이 API를 선언만 해주면 돼요. 다만 API 이름 형식이 JPA가 이해할 수 있는 형태여야 해요.

public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByName(String name);
}

이렇게 선언만 해주면 JPA에서 해당 메소드에 대한 구현체를 알아서 만들어줘요. 여기서 And와 같은 표현도 사용할 수 있고 After와 같은 조건도 지정할 수 있어요.

JPQL과 Querydsl

만약에 복잡한 SQL 쿼리를 작성할 필요가 있따면 JPQL (Java Persistence Query Language) 또는 Querydsl을 사용해요. JPQL은 정의한 Repository 인터페이스에 SQL을 직접 작성하는 방법이고 Querydsl은 Builder 패턴 방식으로 복잡한 쿼리를 Java 코드 형식으로 작성하게 도와주는 라이브러리에요.

JPQL (Java Persistence Query Language)

다음은 JPQL을 사용한 예시에요.

@Query("SELECT m FROM Member m WHERE m.name = :name")
List<Member> findByName(@Param("name") String name);

@Query 어토네이션을 적용해 JPQL을 적용하고 SQL문을 작성해요. SQL문 안에서 변수 사용이 필요하다면 :name과 같이 표현해주고 @Param("name")과 같이 변수를 매핑해요.

JPQL은 기존 Repository에 복잡한 쿼리를 적용하기에 간편한 방법이지만 SQL문을 직접 작성해야한다는 점에서 JPA 취지와 맞지 않을 수 있어요.

Querydsl

다음은 Querydsl 이에요. Querydsl은 Repository에 종속되지 않고 따로 동작해요.

JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QMember member = QMember.member;

List<Member> members = queryFactory
    .selectFrom(member)
    .where(member.name.eq("홍길동"))
    .fetch();    

Querydsl은 빌더 패턴을 통해 SQL과 유사하게 쿼리를 작성할 수 있어요. 쿼리에 오류가 있다면 JPQL과는 다르게 컴파일 시점에 오류를 파악할 수 있는 장점이 있어요.

트랜잭션 관리

데이터베이스의 트랜잭션을 선언적으로 관리할 수 있는 기능을 제공해요. @Transactional 어노테이션을 사용합니다.

@Service
@Transactional
public class MemberService {
    // ...
}

이렇게 하면 MemberService 기능이 수행될 때 데이터의 일관성을 보장할 수 있어요. 다만 트래픽 증가에 따른 처리 시간이 늘어날 수 있으니 주의해야 해요.

Auditing

엔티티의 생성 및 수정 시간을 자동으로 기록해주는 기능이에요. @CreatedDate@LastModifiedDate 어노테이션을 사용해요.

@EntityListeners(AuditingEntityListener.class)
public class Member {
    @CreatedDate
    private LocalDateTime createdAt;
    @LastModifiedDate
    private LocalDateTime updatedAt;
    // ...
}

Pageable

Pageable 인터페이스를 사용하면 페이지를 쉽게 관리할 수 있어요. 페이지 번호와 크기를 지정할 수 있습니다.

Pageable pageable = PageRequest.of(0, 10); // 1페이지, 10개 항목
Page<Member> members = memberRepository.findAll(pageable);

이벤트 리스너

엔티티의 변화를 감지해 특정 작업을 수행할 수 있는 기능을 제공해요.

@Entity
@EntityListeners(MemberEventListener.class)
public class Member {
    // ...
}

public class MemberEventListener {
    @PrePersist
    public void beforePersist(Member member) {
        // ...
    }
}

Cascade

엔티티간의 관계 설정과 부모와 자식 엔티티 간에 변경에 따른 설정을 추가할 수 있어요.

@Entity
public class Order {
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems;
    // ...
}
profile
뭐라도 적자

0개의 댓글