createNativeQuery
- 기본 SQL 쿼리를 실행할 때 사용
createQuery
- JPA가 제공하는 객체지향 쿼리를 실행할 때 사용
- 예시:
select b from Board b join fetch b.user u left join fetch b.replies r left join fetch r.user where b.id = :id order by r.id desc
board_tb
→Board
객체b
→ 별칭?
→:username
inner join
→join fetch
on
→b.user
left outer join
→left join
createNamedQuery
- Query Method
- 함수 이름으로 쿼리를 생성
- 사용하지 않음
createEntityGraph
- 사용하지 않음
createEntityGraph
- 사용하지 않음
persist
- 객체를 영속화 시킴
- id 가 없는 객체는 insert 함
find
- 영속화 된 객체를 가져옴
- 없으면 select 해서 가져옴
1. Persistence Context
1. PC의 위치

2. PC의 기능

User user = joinDTO.toEntity();
// 1. 비영속 객체

em
.persist(user);
// 2. user -> 영속 객체
insert

System.
out
.println(user);
// 3. user -> db와 동기화

2. Lazy Loading
엔티티 내부 연관관계의 객체를 초기에 로딩(조회, select)하지 않고, 연관관계에 있는 객체의 필드값에 접근 할 때 로딩되는 것
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String content;
private Boolean isPublic;
@ManyToOne(fetch = FetchType.LAZY)
private User user;
private Timestamp createdAt;
}
- Board 객체를 조회할 때 User 객체는 로딩되지 않는다
- Board 객체에서 User 객체의 필드 값에 접근할 때 User 객체가 로딩된다
핵심 요약
- 레이지 로딩 주체: Hibernate (JPA 구현체)
- 방법: 프록시 객체 생성 → 실제 접근 시점에 쿼리 실행
- 주의: 영속성 컨텍스트가 닫힌 후(
@Transactional
바깥 등)에 접근하면LazyInitializationException
발생
3. EAGER 과 LAZY
EAGER
→ 연관된 객체의 데이터를 바로 받고 싶을 때
LAZY
→ 받기 싫고 나중에getter
를 호출할 때만 데이터를 받고 싶을 때
4. 양방향 맵핑 @ManyToOne
과 @OneToMany
@ManyToOne
과 @OneToMany
가 서로 걸려있는 경우를 양방향 맵핑이라 한다@ManyToOne (N:1 관계)
- 다수(N)의 엔티티가 하나(1)의 엔티티에 연결되어 있을 때 사용.
- 보통 외래 키는
@ManyToOne
쪽에 존재.
- 예: 여러
Reply
는 하나의Board
에 속함.
public class Reply {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne(fetch = FetchType.LAZY)
private User user;
@ManyToOne(fetch = FetchType.LAZY)
private Board board;
private String content; // 댓글 내용
@CreationTimestamp
private Timestamp createdAt;
}
@OneToMany (1:N 관계)
- 하나(1)의 엔티티가 여러 개(N)의 엔티티를 가짐.
- 외래 키는 반대편(
@ManyToOne
)에 있으므로,mappedBy
속성 필요.
- 주 테이블에는 외래 키가 없고, 연관된 객체 컬렉션만 존재.
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String content;
private Boolean isPublic;
@ManyToOne(fetch = FetchType.LAZY)
// 연관관계 설정 -> ORM 하려고 EAGER -> fk에 들어간 오브젝트를 바로 연관관계 맵핑을 해서 select를 여러 번 한다 , LAZY -> 무조건 LAZY를 사용한다. 연관관계 맵핑을 하지 않는다
private User user;
@OneToMany(mappedBy = "board", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
// mappedBy -> fk 의 주인인 reply의 필드 이름을 적어야 한다
private List<Reply> replies = new ArrayList<Reply>();
@CreationTimestamp
private Timestamp createdA
}
양방향 관계 정리
// Board <--> Reply
// Board: 1개
// Reply: 여러 개
Reply
에서는@ManyToOne
으로Board
를 참조
Board
에서는@OneToMany(mappedBy = "board")
로 컬렉션으로Reply
를 참조mappedBy
로 지정하는 이름은Reply
에서@ManyToOne
로 지정한 변수 이름을 넣는 것이다
양방향 맵핑을 하면 좋은 점
- 이렇게 쿼리를 작성하면
em.createQuery("select b from Board b join fetch b.user u left join fetch b.replies r where b.id = :id", Board.class);
- 이렇게 자동으로 오브젝트 릴레이션 맵핑을 하기 때문

⚠️ 주의사항
1. 연관관계의 주인(owner)
@ManyToOne
쪽이 연관관계의 주인 (즉, DB에 외래 키를 갖고 있음)
@OneToMany
는mappedBy
로 주인을 지정할 뿐, 외래 키를 소유하지 않음
2. 양방향 관계 설정 시 무한 루프 주의 (toString, JSON 직렬화)
- 예:
@ToString.Exclude
,@JsonManagedReference
,@JsonBackReference
등 사용
5. Cascade

@oneToMany
위 어노테이션을 가지고 있는
List<Reply> replies
객체는 여기에 Reply
가 추가 되거나 삭제 되면 트랜잭션이 끝난 다음 insert or delete
쿼리가 작동한다쿼리를 진짜 객체 지향적으로 사용하는 방법
Share article