@Entity
@Data
@NoArgsConstructor
public class Post {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "user_id") // Post 테이블에 user_id(FK) 컬럼 생성
private User user;
}
양방향 매핑 (선택 사항)
@Entity
@Data
@NoArgsConstructor
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "user")
private List<Post> posts = new ArrayList<>();
}
mappedBy = "user": Post의 user 필드가 관계의 주인임을 나타냄 (즉, 외래 키는 Order가 관리함)
@Entity
@Data
@NoArgsConstructor
public class User extends AuditableEntity {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "user_id", insertable = false, updatable = false) // 읽기 전용
private List<Post> posts = new ArrayList<>();; // post.userId = user.id
}
@Entity
@Data
@NoArgsConstructor
public class Post extends AuditableEntity{
@Id
@GeneratedValue
private Long id;
private String content;
@Column(name = "user_id")
private Long userId; // 연관 객체 대신 외래키 값만 저장
}
단방향 @OneToMany에서 @JoinColumn을 사용하는 이유
문제점: Join Table이 자동 생성됨
JPA에서 @OneToMany 단방향을 사용할 때 @JoinColumn을 명시하지 않으면,
JPA는 중간에 조인 테이블 (예: user_post) 를 생성해서 User와 Post를 연결.
import jakarta.persistence.*;
import java.time.LocalDateTime;
public class AuditListener {
@PrePersist
public void perPersist(Object o) {
if (o instanceof Auditable) {
((Auditable) o).setCreateAt(LocalDateTime.now());
((Auditable) o).setUpdateAt(LocalDateTime.now());
}
}
@PreUpdate
public void perUpdate(Object o) {
if (o instanceof Auditable) {
((Auditable) o).setUpdateAt(LocalDateTime.now());
}
}
}
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@NoArgsConstructor
@Data
@EntityListeners(AuditListener.class)
public class Post implements Auditable {
@Id
@GeneratedValue
private Long id;
private String title;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// getter/setter 생략 가능
}
AuditingEntityListener
Spring Boot에서 제공하는 AuditingEntityListener는 Spring Data JPA의 감사(Auditing) 기능을 자동화해주는 리스너 클래스다. 이 리스너를 통해 createdDate, lastModifiedDate, createdBy, lastModifiedBy 같은 필드를 자동으로 관리할 수 있다.
ORM은 Object Relational Mapping의 약자로, 객체 지향 프로그래밍 언어(예: Java)에서 사용하는 객체(엔티티)와 관계형 데이터베이스(RDB)에서 사용하는 테이블 간의 불일치를 해결하고, 이 둘을 자동으로 매핑해주는 기술입니다. 즉, 자바의 객체와 데이터베이스의 테이블을 1:1로 연결해주는 중계자 역할을 하며, 개발자가 객체를 조작하면 그에 맞춰 자동으로 데이터베이스에 반영되도록 해줍니다
@GeneratedValue는 JPA(Java Persistence API)에서 엔티티 클래스의 주요 키(Primary Key) 값을 자동으로 생성하기 위해 사용하는 어노테이션입니다. 주로 @Id와 함께 사용되어, 데이터베이스에 엔티티를 저장할 때 해당 필드의 값을 자동으로 생성하도록 지시합니다
JpaRepository<{Entity 클래스} , {PK 데이터 타입}> 을 상속 받는다.
Application.yml 설정
spring:
jpa:
show-sql: true #jpa 사용 시 sql 로그 출력
properties:
hibernate:
format_sql: true # sql 로그를 sql 포맷형식으로 출력
defer-datasource-initialization: true
h2:
console:
enabled: true
Spring Boot 2.5 이상부터는 기본적으로 data.sql과 같은 SQL 스크립트가 Hibernate(JPA)의 DDL(테이블 생성 등) 작업보다 먼저 실행된다. 이 때문에 Hibernate가 아직 초기화 되지 않았는데 data.sql이 실행되어 에러가 발생할 수 있다.
spring.jpa.defer-datasource-initialization: true를 설정하면 Hibernate의 DDL 작업(엔티티 기반 테이블 생성 등)이 모두 끝난 뒤에 data.sql, schema.sql 등 SQL 스크립트가 실행된다
JPA 주요 메소드 및 네이밍 규칙 정리
JpaRepository가 기본적으로 제공하는 주요 메소드와 Spring Data JPA 쿼리 메소드 네이밍 규칙을 아래와 같이 정리할 수 있습니다.
// 콘솔 결과
pages :Page 2 of 2 containing com.example.bookmanager.domain.User instances
totalElements :5
totalPages :2
Number of elements :2
sort :UNSORTED
size : 3
QueryByExampleExecutor란?
QueryByExampleExecutor는 Spring Data JPA에서 제공하는 인터페이스로, 엔티티의 일부 필드 값(예: name, email 등)을 예시로 삼아 동적 쿼리(Example Query)를 쉽게 생성할 수 있게 해줍니다. 즉, “이런 값이 들어있는 엔티티를 찾아줘”라는 식의 검색을 코드 몇 줄로 구현할 수 있다.
위 방식은 특히 문자열에 관련된 것만 쓸 수 있다는 한계점이 있어서 조금 복잡한 쿼리를 만들 때는