JPA 는 자바 객체와 데이터베이스 테이블을 자동으로 매핑해주는 표준 명세(인터페이스)
인터페이스는 약속된 규격
하버네이트는 JPA 표준 명세를 구현한 대표적인 ORM 프레임워크
프레임워크는 뼈대 또는 구조
객체 지향 프로그래밍 언어의 객체와 관계형 데이터베이스의 테이블을 자동으로 연결(매핑)해주는 기술 또는 도구
package com.example.backend.domain.user.entity;
import java.time.LocalDateTime;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import com.example.backend.domain.user.dto.UserRequestDTO;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "user_user_entity")
// 이 엔티티가 어떤 테이블과 연관돼있는지 나타냄
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "user_seq_gen")
@SequenceGenerator(
name = "user_seq_gen",
sequenceName = "USER_SEQ",
allocationSize = 1)
private Long id;
@Column(name = "username", unique = true, nullable = false, updatable = false)
private String username;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "is_lock", nullable = false)
private Boolean isLock;
@Column(name = "is_social", nullable = false)
private Boolean isSocial;
@Enumerated(EnumType.STRING)
@Column(name = "social_provider_type")
private SocialProviderType socialProviderType;
@Enumerated(EnumType.STRING)
@Column(name = "role_type", nullable = false)
private UserRoleType roleType;
@Column(name = "nickname")
private String nickname;
@Column(name = "email")
private String email;
@CreatedDate
@Column(name = "created_date", updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
@Column(name = "updated_date")
private LocalDateTime updatedDate;
public void updateUser(UserRequestDTO dto) {
this.email = dto.getEmail();
this.nickname = dto.getNickname();
}
}
테이블명을 명시한 이유
-> MariaDB에서 쓰는 방식
MariaDB
id BIGINT NOT NULL AUTO_INCREMENT
ORACLE
CREATE SEQUENCE USER_SEQ
START WITH 1
INCREMENT BY 1
NOCACHE;
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "user_seq_gen")
@SequenceGenerator(
name = "user_seq_gen",
sequenceName = "USER_SEQ",
allocationSize = 1)
private Long id;
// MariaDB
// @GeneratedValue(strategy = GenerationType.IDENTITY)
/* DB별 프로파일 분리해서 사용하는 방법
* @Profile("oracle")
* @GeneratedValue(strategy = GenerationType.SEQUENCE, ...)
*
* @Profile("mariadb")
* @GeneratedValue(strategy = GenerationType.IDENTITY)
* */
@GeneratedValue - ID를 어떻게 만들 것인가
strategy = SEQUENCE -> Oracle SEQUENCE 사용
generator = "user_seq_gen" -> 아래 @SequenceGenerator의 이름
@SequenceGenerator - 어떤 SEQUENCE를 어떻게 쓸 것인가
JPA 내부에서 사용하는 SEQUENCE 설정 이름 name = "user_seq_gen"
@GeneratedValue(generator=...) 와 연결됨
DB랑은 무고나함
sequenceName = "USER_SEQ"
DB에 실제 존재하는 Oracle SEQUENCE 이름
SELECT USER_SEQ.NEXTVAL FROM DUAL;
이 SEQUENCE를 쓰겠다는 뜻
allocationSize = 1
-> Hibernate가 한 번에 미리 가져올 ID 개수
DB
CREATE SEQUENCE USER_SEQ
CACHE 50;
JPA
allocationSize = 50
반드시 맞춰줘야함 -> DB 왕복 횟수 감소
이렇게 사용하는 이유는 대량 INSERT 대응
NOCACHE -> SEQUENCE 값을 미리 메모리에 올리지 않음
CREATE SEQUENCE USER_SEQ
NOCACHE;
괄호 안에 name 은 DB 컬럼명
nullable = false 는 NOT NULL 을 의미
unique = true 는 UNIQUE 제약
updateable = false 는 UPDATE 시 변경 불가
@Column(name = "username", unique = true, nullable = false, updatable = false)
이 설정은 username 절대 중복 불가, 생성 이후 변경 불가
STRING을 쓰는 이유
public enum UserRoleType {
USER, ADMIN
}
이렇게 돼있음 UserRoleType 을 변수 roleType 으로 꺼내서 roleType.USER 이렇게 사용하거나 roleType.ADMIN 으로 사용해서 USER 아니면 ADMIN이 DB에 STRING 형태로 들어가게 하기 위해 씀
0이나 1로 값을 구분하면 의미 파악 힘듦
SocialProviderType
@Getter
public enum SocialProviderType {
NAVER("네이버"),
GOOGLE("구글");
private final String description;
private SocialProviderType(String description) {
this.description = description;
}
}
소셜 로그인 제공자 종류를 제한된 값으로 정의
NAVER : 실제 코드에서 쓰는 값
"네이버" : 설명용 문자열(사람이 읽기 좋게)
Enum 상수 - NAVER
description - 네이버
private final String description;
각 Enum 값이 자기만의 설명을 하나씩 가진다.
final -> 한 번 정하면 변경 불가
Enum 상수마다 고정 값
소셜 로그인 제공자를 코드에서는 NAVER/GOOGLE로 쓰고, 화면에서는 네이버/구글로 보여주기 위한 설계
@EntityListeners(AuditingEntityListener.class)
@EnableJpaAuditing
보통 SpringBootApplication에 추가
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime updatedDate;
모든 필드에 대해 getter 자동 생성
모든 필드를 받는 생성자 생성
빌더 패턴 지원
-> 생성자 파라미터 순서 문제 해결 + 가독성 증가
package com.example.backend.domain.user.entity;
import lombok.Getter;
@Getter
public enum SocialProviderType {
NAVER("네이버"),
GOOGLE("구글");
private final String description;
private SocialProviderType(String description) {
this.description = description;
}
}
package com.example.backend.domain.user.entity;
public enum UserRoleType {
USER, ADMIN
}
package com.example.backend.domain.user.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.backend.domain.user.entity.UserEntity;
public interface UserRepository extends JpaRepository<UserEntity, Long> {
Boolean existsByUsername(String username);
Optional<UserEntity> findByUsernameAndIsLockAndIsSocial(String username, Boolean isLock, Boolean isSocial);
}
UserEntity를 DB에서 조회/저장하는 전용 창구
UserEntity <-> user_user_entity 테이블
기본 CRUD는 이미 JpaRepository가 제공
INSERT, UPDATE, DELETE, PK 조회, 조건 조회 자동
개발자가 SQL 안 씀
Boolean existsByUsername(String username);
자동 생성되는 SQL
SELECT
CASE WHEN COUNT(*) > 0 THEN TRUE ELSE FALSE END
FROM user_user_entity
WHERE username = ?;
있으면 true 없으면 false 회원가입 시 아이디 중복 체크
빠르고 가벼운 조회
Optional<UserEntity> findByUsernameAndIsLockAndIsSocial(String username, Boolean isLock, Boolean isSocial);
자동 생성되는 SQL
SELECT *
FROM user_user_entity
WHERE username = ?
AND is_lock = ?
AND is_social = ?;
반환 타입
Optional<UserEntity>
이유
package com.example.backend.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {
}
Entity의 CreatedDate, LastModifiedDate를 위한 Config 등록
@CreatedDate, @LastModifiedDate 가 실제로 동작하게 만드는 스위치
JPA 엔티티의 생성/수정 시간을 Spring이 자동으로 채워주도록 활성화
이게 없으면 @CreatedDate, @LastModifiedDate 아무 일도 안함
조건 1 : 설정 클래스 등록
@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {}
조건 2 : 엔티티에 리스너 등록
@EntityListeners(AuditingEntityListener.class)
조건 3 : 필드에 Auditing 어노테이션
@CreatedDate
@LastModifiedDate
이 클래스를 Spring 설정 클래스로 등록
Spring이 시작할 때 @EnableJpaAuditing 감지, Auditing 기능 활성화