Spring/강의

[📙 숙련 Spring] 3-3. Spring Data JPA

가지코딩 2025. 5. 21. 00:50

📙 목차

  1. Spring Boot와 JPA
  2. Spring Data JPA
  3. SimpleJpaRepository
  4. Query Methods
  5. JPA Auditing
  6. JPA Auditing 적용하기

1. Spring Boot와 JPA

Spring Boot는 JPA 설정을 자동으로 구성하여 JPA를 쉽게 사용할 수 있도록 도와준다.

 

 

기존 코드

  • 직접 트랜잭션을 사용한다.(JPA는 기본적으로 하나의 트랜잭션 안에서 기능을 수행한다)
  • 직접 EntityManagerFactory 와 EntityManager 를 생성하여 사용한다.
  • 직접 close() 하여 연결을 종료 해야한다.
public static void main(String[] args) {
    // EntityManagerFactory 생성
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
    // EntityManager 생성
    EntityManager em = emf.createEntityManager();
    // Transaction 생성
    EntityTransaction transaction = em.getTransaction();

    // 트랜잭션 시작
    transaction.begin();

    try {
        // 비영속
        Tutor tutor = new Tutor(1L, "wonuk", 100);

        System.out.println("persist 전");
        // 영속
        em.persist(tutor);
        System.out.println("persist 후");

        // transaction이 commit되며 실제 SQL이 실행된다.
        transaction.commit();
    } catch (Exception e) {
        // 실패 -> 롤백
        e.printStackTrace();
        transaction.rollback();
    } finally {
        // 엔티티 매니저 연결 종료
        em.close();
    }
    
    emf.close();

}

 

 

Spring Boot와 JPA

  • JPA는 Spring에 종속적인 것이 아니다.
  • Spring Boot 에서 JPA를 사용하기 위해서는 build.gradle에 의존성 추가가 필요하다.
    • spring-boot-starter-data-jpa
    • 필요한 JPA 설정과 Entity 관리를 자동으로 해준다.
  • 자동으로 내부에서 EntityManagerFactory 를 하나만 생성해서 관리(싱글톤)한다.
    • 자동으로 Bean으로 등록된다.
    • 직접 만들지 않아도 된다.
    • 직접 연결을 close() 하지 않아도 된다.
    • application.properties 에 설정된 DB 정보로 생성된다.
  • @PersistenceContext를 통해 자동으로 생성된 EntityManager를 주입받아 사용할 수 있다.
@Repository
public class TutorRepository {
    
    @PersistenceContext
    private EntityManager em;

    public void save(Tutor tutor) {
        em.persist(tutor);
    }

    public Tutor findById(Long id) {
        return em.find(Tutor.class, id);
    }

    public List<Tutor> findAll() {
        return em.createQuery("SELECT * FROM tutor", Tutor.class).getResultList();
    }

    public void delete(Tutor tutor) {
        em.remove(tutor);
    }
}

2. Spring Data JPA

Spring Data JPA

  • Spring Framework에서 JPA를 쉽게 사용할 수 있도록 제공하는 모듈

 

Spring Data JPA 특징

  • JPA 추상화 Repository 제공
    • CrudRepository, JpaRepository 인터페이스를 제공한다.
    • SQL이나 EntityManager를 직접 호출하지 않아도 기본적인 CRUD 기능을 손쉽게 구현할 수 있다.
  • JPA 구현체와 통합
    • 일반적으로 Hibernate를 통해 자동으로 SQL이 생성된다.
  • QueryMethods
    • Method 이름만으로 SQL을 자동으로 생성한다.
    • @Query 를 사용하여 JPQL 또는 Native Query를 정의할 수 있다.
      • 복잡한 SQL을 직접 구현할 때 사용
  • 트랜잭션 관리와 LazyLoading
    • 트랜잭션 기능을 Spring과 통합하여 제공한다.
    • 연관된 Entity를 필요할 때 로딩하는 지연로딩 기능을 지원한다.

 

 

기존 JPA 방식

  • 반복되는 CRUD 코드가 많다.
public class MemberRepository {

    @PersistenceContext
    private EntityManager em;

    public void save(Member member) {
        em.persist(member);
    }

    public Member findById(Long id) {
        return em.find(Member.class, id);
    }

    public List<Member> findAll() {
        return em.createQuery("SELECT * FROM member", Member.class).getResultList();
    }

    public void delete(Member member) {
        em.remove(member);
    }
}

 

 

Spring Data JPA 방식

  • JpaRepository 상속만으로 CRUD 메서드를 모두 사용할 수 있다.
  • 반복되는 코드가 제거되고 생산성이 높아진다.
public interface MemberRepository extends JpaRepository<Member, Long> {
    Member findById(Long id);
}

3. SimpleJpaRepository

SimpleJpaRepository

  • Spring Data JPA에서 JpaRepository 인터페이스의 기본 구현체
  • JpaRepository를 상속하면, Spring이 내부적으로 이 SimpleJpaRepository 클래스를 사용해 실제 동작을 수행한다.
  • 기본 CRUD 기능을 구현해 놓은 클래스로, 직접 구현하지 않아도 JpaRepository만 상속하면 CRUD 기능이 자동 제공된다.

 


4. Query Methods

Query Methods

  • Spring Data JPA에서 메서드 이름을 기반으로 데이터베이스 쿼리를 자동 생성하는 기능
  • 직접 SQL을 작성하지 않고도 복잡한 쿼리를 쉽게 수행할 수 있게 된다

 

기본 문법

findBy | readBy | getBy | queryBy | countBy | deleteBy | removeBy + 조건절

 

 

예제

public interface UserRepository extends JpaRepository<User, Long> {
    
    // 이름으로 조회
    List<User> findByName(String name);
    
    // 나이가 특정 값보다 큰 사용자 조회
    List<User> findByAgeGreaterThan(int age);
    
    // 이메일이 null이 아닌 사용자 조회
    List<User> findByEmailIsNotNull();
    
    // 이름과 나이 모두 조건으로 조회
    List<User> findByNameAndAge(String name, int age);
    
    // 이름 또는 이메일 조건으로 조회
    List<User> findByNameOrEmail(String name, String email);
    
    // 이름에 특정 문자열 포함하는 사용자 조회 (like '%str%')
    List<User> findByNameContaining(String keyword);
    
    // 정렬과 페이징 지원 가능
    Page<User> findByAgeGreaterThanOrderByAgeDesc(int age, Pageable pageable);
}

 

 

주요 키워드

키워드 의미
And 조건 AND 결합
Or 조건 OR 결합
Is, Equals 같다 (기본 조건)
Between 범위 (>=, <=)
LessThan 작다 (<)
GreaterThan 크다 (>)
Like LIKE 연산
StartingWith 시작하는 문자열 조회
EndingWith 끝나는 문자열 조회
Containing 포함하는 문자열 조회
OrderBy 정렬
IsNull, IsNotNull NULL 여부 체크

 

 

장단점

장점 단점
간단한 조건 조회는 빠르고 편리하다 복잡한 쿼리는 메서드 이름이 너무 길어진다
가독성이 좋다 이름으로 표현하기 어려운 쿼리도 있다
별도 쿼리 작성이 필요 없다 성능 튜닝을 위한 세밀한 제어가 어렵다

 

* Query Methods는 간단하고 반복적인 조회 쿼리를 빠르게 구현할 때 최적이다

* 복잡하거나 최적화가 필요한 쿼리는 @Query 어노테이션이나 Querydsl을 함께 사용하는 것을 권장한다.


5. JPA Auditing

JPA Auditing

  • JPA Entity에 생성일자(createdDate), 수정일자(lastModifiedDate), 생성자(createdBy), 수정자(lastModifiedBy) 같은 감사(audit) 관련 정보를 자동으로 채워주는 기능

 

JPA Auditing 사용 이유

  • 개발자가 일일이 날짜나 사용자 정보를 세팅할 필요 없이 자동으로 기록된다.
  • 데이터 변경 이력을 쉽게 추적 가능해진다.
  • 감사 로그, 변경 이력 관리에 필수적이다.

 

기존 코드

@Entity
public class User {
		@Id
		private Long id;
		private String name;
		private String address;
		// 생성 시간
		private LocalDateTime createdAt;
		// 수정 시간
		private LocalDateTime updatedAt;
}

@Entity
public class Item {
		@Id
		private Long id;
		private String name;
		private String description;
		// 생성 시간
		private LocalDateTime createdAt;
		// 수정 시간
		private LocalDateTime updatedAt;
}

 

 

JPA Auditing 적용예시

@MappedSuperclass
public class BaseEntity{
		@Column(updatable = false)
		private LocalDateTime createdAt;
		private LocalDateTime updatedAt;

		@PrePersist
		public void prePersist(){
			LocalDateTime now = LocalDateTime.now();
			created_at = now;
			updated_at = now;
		}
	
		@PreUpdate
	  public void preUpdate() {
	    updated_at = LocalDateTime.now();
	  }
}

 

 

주요 어노테이션

어노테이션 역할 및 설명
@EnableJpaAuditing JPA Auditing 기능을 활성화한다.
보통 @SpringBootApplication 클래스에 선언한다.
@MappedSuperclass 공통 매핑 정보를 제공하는 부모 클래스에 붙인다.
상속받는 엔티티에 매핑 정보가 공유된다.
@EntityListeners(AuditingEntityListener.class) 엔티티 생명주기 이벤트 리스너를 등록하여 생성/수정 시점에 자동으로 감사 기능을 수행한다.
@CreatedDate 엔티티가 최초 생성된 시점의 날짜를 자동으로 기록한다.
@LastModifiedDate 엔티티가 마지막으로 수정된 시점의 날짜를 자동으로 기록한다.
@Temporal 날짜 타입 필드의 세부 타입(DATE, TIME, TIMESTAMP)을 지정한다.
@CreatedBy 엔티티를 생성한 사용자 정보를 자동으로 기록한다.
AuditorAware<T> 구현체가 필요하다.
@LastModifiedBy 엔티티를 마지막으로 수정한 사용자 정보를 자동으로 기록한다.
AuditorAware<T> 구현체가 필요하다.

 


6. JPA Auditing 적용하기

1) @EnableJpaAuditing 선언

  • @EnableJpaAuditing 어노테이션을 통해 JPA Auditing 기능을 활성화한다.
  • 보통 Spring Boot 애플리케이션의 메인 클래스 상단에 선언한다.
@EnableJpaAuditing
@SpringBootApplication
public class SpringDataJpaApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringDataJpaApplication.class, args);
    }
}

 

 

2) BaseEntity 클래스 생성

  • @MappedSuperclass로 공통 매핑 정보를 제공하는 부모 클래스임을 선언한다.
  • @EntityListeners(AuditingEntityListener.class)를 통해 Auditing 리스너를 등록한다.
  • createdAt 필드는 updatable = false로 수정되지 않도록 설정한다.
  • 생성 시각과 수정 시각을 자동으로 기록한다.
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Temporal(TemporalType.TIMESTAMP)
    private LocalDateTime modifiedAt;
}

 

 

3) BaseEntity 상속하여 엔티티에 적용

  • BaseEntity를 상속받으면 createdAt, modifiedAt 필드가 자동으로 포함된다.
  • 엔티티가 생성 및 수정될 때마다 날짜가 자동으로 업데이트된다.
@Entity
public class User extends BaseEntity {
    @Id
    private Long id;
    private String name;
}