나중에 찾아보고 사용하기 위해 정리합니다. ㅎㅎ
Entity
@Entity
public class user {
@Id @GeneratedValue
private Long id;
}
해당 클래스가 JPA 에서 관리하는 Entity Class 임을 나타냅니다.
해당 Class 는 DB 의 Table 로 Mapping 되며, 기본생성자(public or protected) 가 꼭 필요합니다.
또한 해당 Class 에는 @Id 가 붙은 필드가 하나 이상 있어야 합니다.
다른 코드들을 보면 기본 생성자를 안 넣고 @Entity 를 사용해도 오류가 발생하지 않는데 그 이유는 자바는 생성자를 하나도 안 만들면 컴파일러가 자동으로 기본 생성자를 만들어 주기 때문입니다.
Table
@Entity
@Table(name = "user_table", schema="my_schema")
public class User {
@Id @GeneratedValue
private Long id;
}
테이블명을 직접 지정해주고 싶거나, 스키마를 분리하고 싶을 때 사용합니다.
name : 테이블 이름 ( default 는 Class Name )
schema : 사용할 DB 스키마 이름
uniqueConstraints : 테이블에 유니크 제약조건 설정
uniqueConstraints 옵션은 한 DB 에 동일한 이름을 가진 Table 명이 있을 경우 사용하면 좋습니다.
@Id, @GeneratedValue
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQIENCE, generator = "my_seq_gen")
@SequenceGenerator(
name = "my_seq_gen",
sequenceName="my_seq",
initialValue=1,
allocationSize=30
)
private Long id;
...
}
@Id 는 Entity 의 기본 키(PK) 를 지정해줍니다.
@GeneratedValue 는 어떻게 값을 생성할 지 결정하는 옵션입니다.
대표적인 네가지는 다음과 같습니다.
AUTO : JPA 가 DB 에 맞춰 자동선택 - 거의 모든 DB
IDENTITY : DB 의 AUTO_INCREMENT 사용 - MySQL, MariaDB
SEQUENCE : DB 시퀀스 사용 - ORACLE, PostgreSQL
위에 코드를 보면 SEQUENCE 를 사용하는데, 이 SEQUENCE 를 사용하려면 DB 에 시퀀스가 존재해야 합니다.
CREATE SEQUENCE my_seq START WITH 1 INCREMENT BY 1;
initalValue 는 대부분 사용하지 않습니다. DB 에 직접 시퀀스를 세팅하기 때문입니다.
allocationSize 는 DB 에 미리 30개의 시퀀스 값을 메모리에 적재하고 사용하는 것을 의미합니다.
그럼 DB 에서 계속 통신을 하지 않아 성능면에서 우수합니다.
@Column
@Column(name = "user_name", unique = false, nullable = false)
private String name;
자바 필드와 DB Column 을 Mapping 해주는 기능입니다.
생략하시면 default 값으로 변수명이 Column Name 이 됩니다.
아래는 옵션입니다.
name : DB 컬럼명 지정
nullable : NOT NULL 여부 (true = NULL 허용)
unique : 유니크 제약조건 (true = 중복불가)
length : 문자길이(VARCHAR 의 크기)
insertable/updatable : INSERT/UPDATE SQL 에 포함할지에 대한 여부
precision, scale : 숫자 자릿수 지정
@Enumarated
public enum Role {
USER, ADMIN
}
@Enumarated(EnumType.STRING)
private Role role;
user.setRole(Role.USER); // <-- USER 저장
enum 타입 필드를 DB Column 과 매핑할 때 어떤 방식으로 저장할 지 결정하게 됩니다.
@ManyToOne, @OneToMany, @JoinColumn
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<>();
}
일대다, 다대일 Mapping 을 도와줍니다.
중요한 점은 외래키(PK) 를 누구한테 정의해주냐 입니다.
결론은 반드시 Many 쪽에서 정의해 주어야 합니다.
또한, 위에 코드를 보면 지연로딩(FetchType.LAZY) 를 적용한 모습을 볼수가 있는데, 되도록이면 모든 부분에 지연로딩을 설정해주셔야 합니다. Team 정보도 즉시 가져오기 때문에 불필요한 SQL 문이 여러번 실행 될 수 있습니다. (이러한 문제를 N+1 문제라고 합니다.)
@Inheritance, @DiscriminatorColumn, @DiscriminatorValue
@Entity
@Inheritance(strategy = InheritanceType.JOIND)
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
}
@Entity
public class Book extends Item {
@Id @GeneratedValue
private Long id;
private String author;
}
@Entity
public class Movie extends Item {
@Id @GeneratedValue
private Long id;
private String director;
}
JPA 에서 상속관계를 매핑할 때 사용하는 Annotation 입니다.
즉, Java 클래스가 상속 구조일 때 이를 테이블 구조로 어떻게 매핑 할 것인지 지정합니다.
옵션은 아래와 같습니다.
SINGLE_TABLE
하나의 테이블에서 모든 자식 엔티티를 저장합니다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype") // <-- 생략해도 JPA 에서 알아서 DTYPE 생성
public class Item {...}
결과
id | name | dtype | author | director |
1 | 책1 | Book | 작가A | NULL |
2 | 영화 | Movie | NULL | 감독B |
위에 dtype 을 보면 각 클래스명이 기본값으로 들어가지만 만약 이러한 기본값을 변경하고 싶다면 @DiscriminatorValue 를 사용하시면 됩니다.
@Entity
@DiscriminatorValue("B") // <- 이렇게 설정하면 dtype 에 B 가 들어간다.
public class Book extends Item {...}
JOINED
각 자식마다 테이블을 만들고 부모와 조인합니다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {...}
결과
Item Table | Book Table | Movie Table |
id, name | id, author | id, director |
JOINED 랑 SINGLE_TABLE 이랑 크게 다른 점 중 하나는 SINGLE_TABLE 로 설정한 뒤 movie.setName("공룡") 이라고 입력을 하고 em.persist 로 영속성 컨텍스트에 저장을 하면 Movie Table 의 Name Column 에 공룡이 들어가는게 아니라(Column 이 아예 없기도 함) Item Table 의 Name Column 에 공룡이라는 데이터가 들어가게 됩니다.
@MappedSuperclass
@MappedSuperclass
public abstract class BaseEntity {
private String name;
private String age;
}
@Entity
public class User extends BaseEntity {...}
@Entity
public class Student extends BaseEntity {...}
공통으로 사용하는 변수들을 모아놓고 관리하고 싶을 때 사용합니다.
@MappedSuperclass 는 Entity 가 아니기 때문에 DB 에 적재될 수가 없습니다.
@Embeddable, @Embedded
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
// 기본생성자 필수
}
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@Embedded
private Address address;
}
Entity 안에 들어가는 값 객체입니다.
복잡한 필드를 하나의 클래스로 묶어서 재사용하고 싶을 때 사용합니다.
이 클래스를 테이블로 따로 분리하지 않고, 소속된 엔티티 테이블에 컬럼으로 포함합니다.
@Embedded 는 임베디드 타입을 사용하는 필드 라는 뜻입니다.
실제 DB 에는 address_city, address_street, address_zipcode 로 저장됩니다.
여기서 중요하게 볼 부분은 @Embedded 필드명이 컬럼 접두사로 사용된다는 것입니다.
컬럼명을 커스터마이징 하고 싶을 때는 아래와 같이 @AttributeOverrides 를 사용하시면 됩니다.
@Embedded
@AttributeOverrides ({
@AttributeOverride(name = "city", column = @Column(name = "home_city"))
})
@Embeddable Type 을 사용할 경우 주의해야 할 점이 있습니다.
여러 Entity 에서 공유하고 사용하고 있다면 "값 타입" 특성상 Side Effect 가 발생하게 됩니다.
Embedded Type 은 불변 객체로 설계하는 것이 원칙이라는 규칙때문입니다.
만약 여러 Entity 에서 Address 를 사용하고 있는 경우 중간에 City 값을 변경하게 된다면 다른 Entity 들의 City 값도 함께 변경되게 됩니다. 따라서 혹시나 특정 Entity 의 City 값만 변경하고 싶다면 값 객체를 공유하지 말고, 복사해서 사용하시면 됩니다.
member.setAddress(new Address("Seoul", "강남대로", "1234"))
user.setAddress(new Address("Busan", "서면", "1234"))
JPA 는 값 타입 컬렉션이라는 기능을 지원해줍니다.
말 그대로 값 타입(@Embeddable 로 정의된 타입) 을 컬렉션(List, Set..) 형태로 여러 개 저장하는 것을 의미합니다.
@ElmentCollection 을 사용하는 방법입니다. 하지만 이러한 값 타입 컬렉션은 제약사항이 많습니다.
1. 지연 로딩만 지원됨 (LAZY)
2. 별도의 테이블이 만들어짐
3. 식별자가 없음 -> 개별 요소르 식별하거나 직접 수정하기 어려움
4. 수정하면 통으로 삭제 후 재삽입 해야함
따라서 값 타입 컬렉션을 사용하는 것 보다 Entity 로 분리하고, @OneToMany 로 Mapping 하고, @Id 를 추가해서 사용하면 개별 수정이 가능해지기 때문에 이러한 방법을 추천합니다.
CASCADE
Life Cycle 이 똑같을 때 사용합니다.
부모 -> 자식 관계에서 다른 부모도 해당 자식을 참조하는게 여러개가 존재한다면 사용하시면 안됩니다.
@Entity
public class Child {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Parent parent;
}
@Entity
public class Parent {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> childList = new ArrayList<>();
public void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
// 다른곳에서
Child child1 = new Child();
Child child2 = new Child();
// em.persist(child1) <-- 원래 이렇게 영속성 컨텍스트에 저장해야함.
// em.persist(child2) <-- 원래 이렇게 영속성 컨텍스트에 저장해야함.
Parent parent = new parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
'Spring' 카테고리의 다른 글
Spring 에서 CORS 설정 (3) | 2025.07.09 |
---|---|
@Aspect - AOP (0) | 2025.05.02 |
@Aspect 쓰지 않고 순수 Proxy, Proxy Factory 사용법 (0) | 2025.04.29 |
다양한 패턴 (0) | 2025.04.25 |
트랜잭션 프록시와 예외 (0) | 2025.04.08 |