@OneToMany와 @ManyToOne은 JPA에서 객체 관계 매핑을 설정할 때 사용되는 애너테이션으로, 데이터베이스 테이블 간의 관계를 객체 모델에서 표현하는 방법입니다. 각각의 차이점과 사용해야 하는 상황을 설명하겠습니다.
의미 : 한 엔티티가 여러 다른 엔티티와 관계를 가질 때 사용합니다.
예: 부모 엔티티가 자식 엔티티를 여러 개 가질 수 있음.
매핑 방향
사용해야 하는 상황
부모-자식 관계
예: 부모(Parent)가 여러 자식(Child)을 가짐.
컬렉션 데이터를 필요로 할 때
예: 한 사용자(User)가 여러 게시글(Post)을 작성할 때.
의미 : 여러 엔티티가 하나의 엔티티와 관계를 가질 때 사용합니다.
예 : 여러 자식 엔티티가 같은 부모 엔티티를 참조할 수 있음.
매핑 방향
단방향: 자식 엔티티가 부모를 참조.
양방향: 자식 엔티티가 부모를, 부모 엔티티가 자식 리스트를 참조.

양방향 매핑은 객체 간의 관계를 보다 직관적으로 표현할 수 있지만, 관리해야 할 코드 복잡도가 늘어날 수 있습니다.
→ 정합성 유지를 위해 연관 관계 편의 메서드를 작성하세요.
단순한 참조가 필요하다면 @ManyToOne 단방향 매핑을 고려하세요.
Lazy Loading을 기본적으로 설정하고, 필요한 경우에만 데이터를 Fetch하여 성능 문제를 방지하세요.
@JoinColumn(name = "parent_id")를 사용하면 JPA는 자식 테이블에 parent_id라는 외래 키 컬럼을 생성합니다. 이 컬럼은 부모 엔티티(Parent)의 기본 키 값을 저장합니다.
@JoinColumn은 외래 키가 저장될 컬럼 이름을 명시적으로 설정합니다.
외래 키는 자식 테이블에 생성되며, 부모 테이블의 기본 키를 참조합니다.
@JoinColumn을 생략하면 JPA는 기본적으로 부모 엔티티의 이름 + _id 형식으로 외래 키 컬럼 이름을 생성합니다.
외래 키를 통해 부모-자식 관계를 매핑하고, 이를 통해 두 엔티티 간의 참조를 설정합니다.
부모 엔티티의 클래스 이름 + _id 형식으로 외래 키 컬럼 이름이 결정됩니다.예: Parent 엔티티 → parent_id.
외래 키는 주로 자식 테이블에 존재하며, 부모 테이블의 기본 키(Primary Key)를 참조하는 방식으로 데이터베이스 관계를 유지합니다. 부모 엔티티는 외래 키 값을 직접 저장하지 않고, 자식 엔티티를 참조하는 역할만 합니다.
관계의 방향성
부모-자식 관계에서의 책임 분리
부모는 자신이 가지고 있는 데이터만 관리하고, 자식 테이블은 외래 키를 통해 부모와의 관계를 관리합니다.
이는 데이터베이스 설계에서 효율적이고 직관적인 관계 설정 방식입니다.
관계형 데이터베이스의 정규화 원칙에 따라, 외래 키는 관계를 정의하는 데 필요한 최소한의 정보를 사용하여 테이블 간 중복을 방지합니다.

JPA에서는 엔티티 간의 연관 관계를 설정할 때 기본적으로 @Id가 지정된 필드를 참조하도록 설계되어 있습니다.
만약 @id가 아닌 다른 컬럼을 참조하고 싶다면, @JoinColumn의 referencedColumnName 속성을 사용하여 참조할 컬럼을 명시적으로 지정해야 합니다.
@ManyToOne
@JoinColumn(name = "user_username", referencedColumnName = "username")
private User user;
name = "user_username": 외래 키 컬럼 이름을 지정합니다.
referencedColumnName = "username" : User 엔티티의 username 컬럼을 참조하도록 설정합니다.
생성되는 테이블
Post 테이블의 외래 키 컬럼은 user_username으로 생성됩니다.
외래 키는 User 테이블의 username 컬럼을 참조합니다.
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List posts = new ArrayList<>();
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Setter
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Post> posts = new ArrayList<>();
// Helper method to manage bidirectional relationship
public void addPost(Post post) {
posts.add(post);
post.setUser(this);
}
public void removePost(Post post) {
posts.remove(post);
post.setUser(null);
}
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
}
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
public interface PostRepository extends JpaRepository<Post, Long> {
}
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final PostRepository postRepository;
@Transactional
public void createUserWithPosts() {
// User 생성
User user = new User();
user.setUsername("John Doe");
// Post 생성
Post post1 = new Post();
post1.setTitle("First Post");
post1.setContent("This is the first post.");
Post post2 = new Post();
post2.setTitle("Second Post");
post2.setContent("This is the second post.");
// 관계 설정
user.addPost(post1);
user.addPost(post2);
// 저장
userRepository.save(user);
}
}
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/create")
public String createData() {
userService.createUserWithPosts();
return "User and posts created successfully!";
}
}