1. ORM이란?
ORM(Object-Relational Mapping)은 객체 지향 프로그래밍 언어에서 관계형 데이터베이스를 쉽게 사용할 수 있도록 해주는 기술입니다. ORM을 사용하면 데이터베이스의 테이블과 같은 관계형 구조를 객체 지향 프로그래밍 언어에서 사용하는 객체로 매핑하여, 데이터베이스 작업을 더 직관적이고 간편하게 수행할 수 있습니다.

Hibernate가 ORM의 예시로 사용되고, 그림과 같이 Repository와 DB사이에서 역할을 수행한다
2. ManyToOne 설정
테이블 간의 조인을 설정할 때, ManyToOne 어노테이션을 사용한다. 이때, 어노테이션의 설정은 두 가지가 있는데 LAZY와 EAGER가 있다. 다음 코드와 같이 조인하는 테이블의 Foreign Key의 어노테이션으로 사용한다.
@NoArgsConstructor
@Setter
@Getter
@Table(name="board_tb")
@Entity
public class Board {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Integer id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String content;
private Timestamp createdAt;
// fk
@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.EAGER)
private User user;
@Builder
public Board(Integer id, String title, String content, Timestamp createdAt) {
this.id = id;
this.title = title;
this.content = content;
this.createdAt = createdAt;
}
}
Board 테이블에 User 테이블을 @ManyToOne 으로 Join 시킨고, 타입은 User 타입으로 테이블을 조인 시킬 수가 있다.
3. EAGER 방식
fetch를 설정해주지 않는다면 기본적으로 EAGER 방식으로 설정된다
EAGER방식은 객체 타입을 전부 ORM 하여 가져오기 때문에, Board 테이블을 select 해서 가져올 때, 그에 맞는 User 테이블을 가져오는데 다음 그림과 같다.

public List<Board> findAll(){
Query query = em.createNativeQuery("select * from board_tb order by id desc", Board.class);
List<Board> boardList = query.getResultList();
return boardList;
}4. LAZY 방식
EAGER와 다른 방식으로는 LAZY가 있다.
LAZY방식은 객체 타입을 전부 ORM 하여 가져오지 않고, Board 테이블에 존재하는 user_id 값을 그대로 가져오고 그 이상으로 select 해오지 않는다.


Board 테이블만 조회하고 user_id만 가져온다
따라서, 많은 양의 데이터를 조회할 때, EAGER 방식을 사용하게 되면 불필요한 select를 많이 하게 되므로 LAZY 방식을 사용하고 JOIN 하는 것이 부하가 작게 걸린다.
5. JPQL(Java Persistence Query Language)란?
Repository 에서 query를 쓰는 방식은 여러 개가 존재한다.
1)
public void deleteById(int id){
Query query = em.createNativeQuery("delete from board_tb where id = ? ");
query.setParameter(1, id);
query.executeUpdate();
}
2)
public Board findById(int id){
// select * from board_tb bt inner join user_tb ut on bt.user_id = ut.id where bt.id = 1
Query query = em.createQuery("select b from Board b join fetch b.user where b.id = :id", Board.class);
query.setParameter("id", id);
Board board = (Board)query.getSingleResult();
return board;
}위의 코드처럼 createNativeQuery 방식과 createQuery 등 다양한 방식이 존재한다.
()안의 쿼리문 또한 문법이 다양하다. 1) deleteById 메서드에서 사용하는 쿼리문은 일반적이지만,
2) findById에서 사용하는 쿼리문은 JPQL(Java Persistence Query Language) 이다.
JPQL은 일반 쿼리문과 다르게 테이블명 대신 @Entity 가 사용된 클래스에서 Foreign Key로 가져온 테이블을 객체로 받아와 해석해준다.
1) 쿼리문에서는 테이블 명이 board_tb로 사용하였지만, 2) 쿼리문에서는 Board로 객체를 받아와서 사용된 것을 알 수 있다. 또한 join on 문법이 아니라 join fetch(=inner join)을 사용하여 on 절의 조건을 사용하지 않아도 되는 장점이 있다. join fetch 앞에 left나 right 를 붙이게 되면 outer join의 역할을 하게 된다.
또한, JPQL을 사용하면 DB가 Oracle인지 , mysql, 마리아DB에 상관하지 않고 객체를 DB에 맞게 해석해주는 기능도 가지고 있다.
6. 클래스 매핑의 차이
1) 과 2) 의 또 다른 차이점은 쿼리문 뒤의 Board.class의 유무이다.
여기서 Board.class 자리는 Hibernate 또는 JPA가 쿼리 결과를 특정 엔티티 클래스에 매핑하도록 하는 지시사항이다. 이는 앞선 쿼리문에 속해 있는 Board를 클래서에서 인스턴스로 매핑해주는 것이다.
public Board findByIdV2(int id) {
Query query = em.createNativeQuery("select bt.id, bt.title, bt.content, bt.user_id, bt.created_at, ut.id u_id, ut.username, ut.password, ut.email, ut.created_at u_created_at from board_tb bt inner join user_tb ut on bt.user_id = ut.id where bt.id = ?");
query.setParameter(1, id);
Object[] obs = (Object[]) query.getSingleResult();
System.out.println(obs[0]);
System.out.println(obs[1]);
System.out.println(obs[2]);
System.out.println(obs[3]);
System.out.println(obs[4]);
System.out.println(obs[5]);
System.out.println(obs[6]);
System.out.println(obs[7]);
System.out.println(obs[8]);
System.out.println(obs[9]);
// 1
// 제목1
// 내용1
// 1
// 2024-08-21 12:49:35.197432
// 1
// ssar
// 1234
// ssar@nate.com
// 2024-08-21 12:49:35.194432
Board board = new Board();
User user = new User();
board.setId((Integer) obs[0]);
board.setTitle((String) obs[1]);
board.setContent((String) obs[2]);
board.setCreatedAt((Timestamp) obs[4]);
user.setId((Integer) obs[3]);
user.setUsername((String) obs[6]);
user.setPassword((String) obs[7]);
user.setEmail((String) obs[8]);
user.setCreatedAt((Timestamp) obs[9]);
board.setUser(user);
return board;
}이것은 Board.class를 설정해주지 않아서 전체 코드를 작성한 예시이다.
클래스를 지정해주지 않았기 때문에 테이블과 기타 코드들을 배열로 묶고 이를 다시 set하여 개체를 지정해준 것이다.
이를 Board.class를 추가하면 간단하게 코딩이 가능하다.
public Board findByIdV2(int id) {
Query query = em.createNativeQuery("select bt.id, bt.title, bt.content, bt.user_id, bt.created_at, ut.id u_id, ut.username, ut.password, ut.email, ut.created_at u_created_at from board_tb bt inner join user_tb ut on bt.user_id = ut.id where bt.id = ?", Board.class);
query.setParameter(1, id);
Board board = (Board) query.getSingleResult();
return board;
}Share article