SpringBoot 환경에서는
application.properties에 DB 정보를 전달해 주면 이를 토대로 EntityManagerFactory와 EntityManager를 자동으로 생성해준다.
@PersistenceContext
EntityManager em;
@PersistenceConext 애너테이션을 사용하면 자동으로 생성된 EntityManager를 주입받아 사용할 수 있다.
# application.properties
spring.application.name=crud
spring.datasource.url=jdbc:mysql://localhost:3306/crud
spring.datasource.username=root
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
기존 생성된 테이블을 삭제하고 새로 생성
spring.jpa.hibernate.ddl-auto=create
종료 시점에 테이블을 삭제
spring.jpa.hibernate.ddl-auto=create-drop
변경된 부분만 반영
spring.jpa.hibernate.ddl-auto=update
엔티티와 테이블 매핑이 정상인지 확인
spring.jpa.hibernate.ddl-auto=validate
-
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
JPA를 사용하여 DB에 데이터를 저장, 수정, 삭제 하려면 트랜잭션 적용이 반드시 필요하다.
@Transactional 애너테이션을 클래스나 메서드에 추가하면 해당 클래스나 메서드 내에서 수행되는 모든 DB 연산 내용은 하나의 트랜잭션으로 묶여 수행된다.
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
...
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
...
}
@Transactional(readOnly = true)
트랜잭션에서 데이터를 읽기만 할 때, 읽기 작업에 대한 최적화를 수행한다.
해당 트랜잭션에서 데이터를 수정하려고 하면 예외가 발생한다.
스프링 컨테이너 환경에서는 영속성 컨텍스트와 트랜잭션의 생명주기가 일치한다.
즉, 트랜잭션이 유지되는 동안은 영속성 컨텍스트도 계속 유지가 되기 때문에 영속성 컨텍스트의 기능을 사용할 수 있다.
Spring이 Service 부터 Repository 까지 Transaction을 유지하는 방법
@Transactional(propagation = Propagation...)
부모 메서드에 트랜잭션이 존재하면, 자식 메서드의 트랜잭션이 부모의 트랜잭션으로 합류하게 된다.

부모 메서드가 종료될 때 커밋이 된다.
Spring Data JPA는 JPA를 더 쉽고 편리하게 사용할 수 있도록 도와주는
Spring Data의 하위 모듈로, 표준화된 Repository 계층을 JPA상에서 자동으로 구현해준다.
기존에 EntityManager를 직접 사용하거나 JdbcTemplate 기반으로 작성하던 Repository 계층 데이터 접근 로직 대신,
JpaRepository를 상속한 Repository 인터페이스만 작성 하면,
Spring Data JPA가 런타임에 기본 구현체인 SimpleJpaRepository를 기반으로 트랜잭션 및 예외 변환 등이 적용된 프록시 객체를 생성하고,
이를 Spring Bean으로 등록하여 사용 가능하게 한다.
Spring Data JPA에서는
JpaRepository<엔티티 클래스, @Id 타입>을 상속받는 인터페이스를 선언하여
Repository를 생성한다.
public interface MemoRepository extends JpaRepository<Memo, Long> {
}
런타임 시 Spring Data JPA가 자동으로 구현체를 생성하여 Bean으로 등록해준다.
JpaRepository에는 기본적인 CRUD 메서드가 이미 정의되어 있어
별도의 구현 없이 바로 사용 가능하다.
save(entity)
findAll()
findById(id)
save(entity) - 영속 상태의 엔티티 값을 변경하면 UPDATE 쿼리 실행
delete(entity)
deleteById(id)
Query Methods는 메서드 이름을 분석하여 JPQL 쿼리를 자동 생성·실행하는 Spring Data JPA 기능이다.
정해진 네이밍 규칙에 맞게 메서드를 선언하면,
Spring Data JPA가 해당 메서드 이름을 분석하여 SimpleJpaRepository 구현체에 쿼리를 자동으로 생성한다.
public interface MemberRepository extends JpaRepository<Member, Long> {
Member findByUsername(String username);
}
Spring Data JPA는 위 메서드를 내부적으로 다음과 같은 JPQL로 해석한다.
select m from Member m where m.username = :username
@Query("select m from Member m where m.age >= :age")
List<Member> findAdult(@Param("age") int age);
DB의 테이블이 아닌 엔티티 기준으로 쿼리한다.
실행 시 DB에 맞는 SQL로 JPA가 변환하여 처리한다.
DB 고유 기능또는 DB 최적화가 필요하면 JPQL 보단 Native Query를 사용하도록 한다.
@Query(
value = "select * from member where age >= ?",
nativeQuery = true
)
List<Member> findAdult(int age);
findByUsernameAndAgeGreaterThanAndStatusOrderByCreatedAtDesc <-???????
* 서브쿼리
* 복잡한 JOIN
* CASE WHEN
* GROUP BY / HAVING
@Query는 Repository 메서드에 직접 JPQL 또는 SQL을 작성할 수 있도록 제공되는 기능이다.
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.username = :username")
Member findByUsername(@Param("username") String username);
}
Spring Data JPA에서 엔티티의 생성/수정 시간 같은 공통 필드를 자동으로 관리해 주는 기능
Auditing: 감사, 검증
@SpringBootApplication 이 있는 class에 @EnableJpaAuditing을 추가한다.
@EnableJpaAuditing
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
해당 엔티티(또는 상위 클래스)에 Auditing 기능을 활성화
엔티티가 처음 생성되어 저장될 때의 시간이 자동으로 저장됩니다.
NSERT 시점에만 동작하므로,
엔티티 수정 시에는 값이 자동으로 변경되지 않는다.
@Column(updatable = false) → UPDATE 쿼리에서 해당 컬럼을 제외하여 (생성 시간이) 변경되지 않도록 보장엔티티가 수정될 때마다 변경된 시간이 자동으로 저장
날짜 타입(java.util.Date, java.util.Calendar)을 매핑
SQL 타입
DATE : ex) 2026-02-02
TIME : ex) 16:21:14
TIMESTAMP : ex) 2026-02-02 20:22:38.771000
createdAt, modifiedAt과 같이 공통 필드를 상속용으로 사용하는 부모 클래스를 만들 때 @MappedSuperclass 애너테이션을 사용한다.
이는 엔티티가 아님을 나타내며, 테이블은 생성되지 않고 자식 엔티티 클래스에 필드만 상속되도록 한다.
(abstract 키워드를 쓰지 않아도 동작은 하지만, 코드 관리상 써 주는게 좋다.)
해당 추상 클래스를 상속받은 JPA 엔티티 클래스들은
추상 클래스에 선언된 필드들을 자신의 컬럼으로 인식하게 된다.
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {
@CreatedDate
@Column(updatable = false) // UPDATE 방지
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime createdAt;
@LastModifiedDate
@Column
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime modifiedAt;
}