Learn & Record
Spring Boot (JPA/ORM, JPA 사용 시 장점, JPA 사용 시 단점, 엔티티, 엔티티 매니저 팩토리, 엔티티 매니저, 영속성 컨텍스트, Board 엔티티와 JpaRepository, Select 기능 테스트, update 기능 테스트) 본문
Spring Boot (JPA/ORM, JPA 사용 시 장점, JPA 사용 시 단점, 엔티티, 엔티티 매니저 팩토리, 엔티티 매니저, 영속성 컨텍스트, Board 엔티티와 JpaRepository, Select 기능 테스트, update 기능 테스트)
Walker_ 2024. 5. 9. 11:261.JPA / ORM
- ORM : 자바와 같은 객체지향 언어에서 의미하는 RDB Relational Database의 테이블을 자동으로 매핑하는 방법
- 클래스는 데이터베이스의 테이블과 매핑하기 위해 만들어진 것이 아니기 때문에 RDB 테이블 어쩔 수 없는 불일치 존재
- ORM이 이 둘의 불일치와 제약사항을 해결하는 역할
- ORM을 이용하면 쿼리문이 아닌 코드(메서드)로 데이터 조작 가능
- JPA : ORM 기술 표준으로 채택된 인터페이스 모음
- ORM이 큰 개념이라면 JPA는 더 구체화된 스펙을 포함
- 즉 JPA 또한 실제로 동작하는 것이 아니고 어떻게 동작해야 하는지 메커니즘을 정리한 표준 명세
2. JPA 사용 시 장점
- 특정 데이터베이스에 종속되지 않음
- 객체지향적 프로그램
- 생산성 향상
3. JPA 사용 시 단점
- 복잡한 쿼리 처리
- 성능 저하 위험
- 학습 시간
4. 엔티티
- 데이터베이스의 테이블에 대응하는 클래스
- @Entity가 붙은 클래스는 JPA에서 관리하며 엔티티라고 함
- 클래스 자체나 생성한 인스턴스도 엔티티라 부름
5. 엔티티 매니저 팩토리
- 엔티티 매니저 인스턴스를 관리하는 주체
- 애플리케이션 실행 시 한 개만 만들어지며 사용자로 부터 요청이 오면 엔티티 매니저 팩토리로 부터
- 엔티티 매니저르 생성
6. 엔티티 매니저
- 영속성 컨텐스트에 접근하여 엔티티에 대한 데이터베이스 작업을 제공.
- 내부적으로 데이터베이스 커넥션을 사용해서 데이터베이스에 접근
7. 영속성 컨텍스트
- JPA를 이해하기 위해서는 영속성 컨텍스트를 이해하는 것이 가장 중요
- 엔티티를 영구 저장하는 환경으로 엔티티 매니저를 통해 영속성 컨텍스트에 접근
8. Board 엔티티와 JpaRepository

- 패키지와 클래스 생성
package com.example.spring_boot_project.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long bno;
private String title;
private String content;
private String writer;
}
- 자바 파일에 위 코드 작성

- domain 폴더에 BaseEntity 파일 추가
package com.example.spring_boot_project.domain;
import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
@MappedSuperclass
@EntityListeners(value = {AuditingEntityListener.class})
@Getter
public class BaseEntity {
@CreatedDate
@Column(name="regdate", updatable = false)
private LocalDateTime regDate;
@LastModifiedDate
@Column(name = "moddate")
private LocalDateTime modDate;
}
- BaseEntity 파일에 위 코드 추가
package com.example.spring_boot_project;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@SpringBootApplication
@EnableJpaAuditing
public class SpringBootProjectApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootProjectApplication.class, args);
}
}
- 메인 메서드에 @EnableJpaAuditing 어노테이션 추가
package com.example.spring_boot_project.domain;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Board extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long bno;
@Column(length = 500, nullable = false) // 컬럼의 길이와 null 허용 여부
private String title;
@Column(length = 2000, nullable = false)
private String content;
@Column(length = 50, nullable = false)
private String writer;
}
- Board.java에 코드 추가

- repository 패키지 생성 후 > BoardRepository 인터페이스 파일 생성
package com.example.spring_boot_project.repository;
import com.example.spring_boot_project.domain.Board;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BoardRepository extends JpaRepository<Board, Long> {
}
- 위 코드 작성

- 테스트에 폴더와 파일 생성
package com.example.spring_boot_project.repository;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@Log4j2
public class BoardRepositoryTests {
@Autowired
private BoardRepository boardRepository;
}
- 위 코드 작성
package com.example.spring_boot_project.repository;
import com.example.spring_boot_project.domain.Board;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@Log4j2
public class BoardRepositoryTests {
@Autowired
private BoardRepository boardRepository;
@Test
public void testInsert() {
for (int i = 1; i <= 100; i++) {
Board board = Board.builder()
.title("title...")
.content("content..." + i)
.writer("uesr" + (i % 10))
.build();
Board result = boardRepository.save(board);
log.info("BNO: " + result.getBno());
}
}
}
- 테스트 코드 작성

- 테스트 실행 후 로그 확인

- 데이터 베이스에 테이블 생성되고 > 데이터 추가 된 지 확인
9. Select 기능 테스트
@Test
public void testSelect() {
Long bno = 100L;
Optional<Board> result = boardRepository.findById(bno);
Board board = result.orElseThrow();
log.info(board);
}
- Select 기능 테스트 코드 추가

- 테스트 실행 후 위 처럼 코드 확인. 정상 작동.
10. update 기능 테스트
- update 기능은 insert와 동일하게 save()를 통해서 처리
package com.example.spring_boot_project.domain;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Board extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long bno;
@Column(length = 500, nullable = false) // 컬럼의 길이와 null 허용 여부
private String title;
@Column(length = 2000, nullable = false)
private String content;
@Column(length = 50, nullable = false)
private String writer;
public void change(String title, String content) {
this.title = title;
this.content = content;
}
}
- Board.java 파일에 위 메서드 코드 추가
@Test
public void testUpdate() {
Long bno = 100L;
Optional<Board> result = boardRepository.findById(bno);
Board board = result.orElseThrow();
board.change("update... title 100", "update content 100");
boardRepository.save(board);
}
- 업데이트 테스트 코드 추가

- Test 실행 후 Update 정상 작동 확인
@Test
public void testUpdate3() {
// 없는 bno를 지정한 경우
Long bno = 1000L;
Board board = Board.builder()
.bno(bno)
.title("title...")
.content("content...update3")
.writer("uesr..update")
.build();
boardRepository.save(board);
}
- 없는 @id 값을 지정하면 update가 아니라 insert가 실행.
공부 과정을 정리한 것이라 내용이 부족할 수 있습니다.
부족한 내용은 추가 자료들로 보충해주시면 좋을 것 같습니다.
읽어주셔서 감사합니다 :)