JPA 의 구현체인 Hibernate 는 Entity 코드를 스캔하여 스키마와 테이블을 자동으로 생성해주는 기능이 있다.
- DDL 을 애플리케이션 실행 시점에 자동으로 생성해주는 기능이다.
- 위 기능은 jpa.hibernate.ddl-auto 옵션을 통하여 설정할 수 있다.
jpa.hibernate.ddl-auto
ddl-auto 기능은 각 RDBMS (MySQL, Oracle, MariaDB, 등)의 서로 다른 방언에 맞는 DDL 을 자동 생성 및 실행해준다.
자동 생성에 대한 옵션은 아래와 같다.
create
- 기존 테이블 삭제 후 다시 생성(DROP + CREATE)
- 초기 개발 시 적용
create-drop
- create와 같으나 종료시점에 테이블 DROP
- 초기 개발 또는 테스트 시 적용
update
- 변경된 부분만 반영된다. (ALTER)
- 테스트 시 적용
validate
- 엔티티와 테이블이 정상 매핑되었는지만 확인한다.
(즉, 테이블의 설정을 변경하지 않는다.) - 테스트 또는 운영 시 적용
none
- DDL 문장을 자동 생성 및 실행 하지 않는다.
- 운영 시 적용
어노테이션
1. @Entity
속성 | 설명 |
name | 1. Entity 클래스와 테이블 이름을 지정 2. 기본값은 클래스명 |
catalog | 1. DB 카탈로그에 엔티티 매핑 가능 |
schema | 1. DB 카탈로그 및 스키마와 연결 할 수 있다. |
@Entity(name = "my_table")
@Entity(name = "my_table", catalog = "my_catalog")
@Entity(name = "my_table", schema = "my_schema")
2. @Table
속성 | 설명 |
name | 1. 테이블 이름 지정 2. 기본값은 엔티티 클래스명 |
catalog | 1. DB 카탈로그에 엔티티 매핑 가능 |
schema | 1. DB 카탈로그 및 스키마와 연결 할 수 있다. |
uniqueCDonstraints | 1. 테이블에 대한 고유 제약 조건을 정한다. |
@Table(name = "my_table")
@Table(name = "my_table", catalog = "my_catalog")
@Table(name = "my_table", schema = "my_schema")
@Table(name = "my_table", uniqueConstraints = @UniqueConstraint(columnNames = {"column1", "column2"}))
1). @Entity와 @Table의 차이점
- @Entity = Entity + Table
- 따로 사용 시 각자 설정 값에 따라 작동
- @Entity(name ="")의 경우 말그대로 엔티티의 이름을 정할때 사용됩니다. 이는 HQL에서 엔티티를 식별할 이름을 정합니다.
- @Table(name ="")의 경우 Database에 생성될 table의 이름을 지정할때 사용됩니다.
- @Table이 없고 @Entity(name ="")만 존재하는 경우, @Entity의 name 속성에 의해, Entity와 Table 이름이 모두 결정됩니다.
3. @Id
JPA Entity Class에서 PK 필드를 지정할 때 사용된다. @Id에는 속성이 없으며 그 자체로 사용되고 @GeneratedValue와 함께 사용하여 자동 생성 옵션을 정할 수 있다.
@Id
private Long id; // 기본 키 필드
4. @GeneratedValue
@Id와 함께 pk 자동 생성 옵션을 정할 때 사용한다.
strategy | GenerationType.IDENTITY | 1. DB가 자동으로 값을 할당 2. 주로 MySQL에서 사용 |
strategy | GenerationType.SEQUENCE | 1. DB 시퀀스를 사용하여 값을 할당 2. 주로 Oracle에서 사용 |
strategy | GenerationType.TABLE | 1. DB 테이블 값을 사용 2. 시퀀스를 사용하지 않고 기본키를 생성 |
strategy | GenerationType.AUTO | 1. DB에 따라 자동 선택 |
generator | 1. GenerationType.TABLE일 때 이름 지정, 특정 상황에만 설정 | |
name | 1. GenerationType.TABLE일 때 사용 | |
allocationSize | 1. DB 시퀀스를 사용할 때 한 번 할당 크기를 지정 2. 시퀀스에 범위의 값을 할당하고 범위 내의 값을 사용 |
MySQL에서 자동 증가 ID를 사용하는 경우 GenerationType.IDENTITY 이외에 설정할 필요가 없습니다.
하지만 Oracle은 GenerationType.SEQUENCE 선택하고 allocationSize 옵션을 선택하고 시퀀스 이름 및 사이즈를 지정해야 합니다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // MySQL에서 자동 증가 ID로 사용
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_sequence")
@SequenceGenerator(name = "my_sequence", sequenceName = "my_seq", allocationSize = 1)
private Long id; // Oracle에서 시퀀스를 사용
5. @Column
name | 1. 컬럼 이름을 지정 2. 없으면 필드 이름이 기본 컬럼 이름으로 지정 |
nullable | 1. NULL 허용 여부 2. 기본값 = true 3. NotNull = false |
length | 1. 문자열 컬럼의 최대 길이 지정 2. 문자열 타입만 적용 |
unique | 1. 컬럼 값이 고유해야되는지 여부 지정 2. ture로 설정 시 컬럼에 중복된 값 허용 X |
updatable | 1. 컬럼 값 업데이트 가능 여부 지정 2. false로 설정 시 해당 컬럼 업데이트 X |
insertable | 1. 컬럼 값 삽입 가능 여부 지정 2. false로 설정 시 해당 값 지정 안해도됨 |
columnDefinition | 1. 컬럼의 유형 또는 제약 조건 정의 가능 [자세한건 예시 코드] |
table | 엔티티 클래스가 여러 테이블에 매핑될 때 특정 테이블 컬럼을 가르킨다 |
@Column(name = "first_name")
private String firstName; // 데이터베이스 컬럼 이름을 "first_name"으로 지정
@Column(nullable = false)
private String username; // NULL 값을 허용하지 않음
@Column(length = 50)
private String email; // 최대 50자까지 허용
@Column(unique = true)
private String email; // 고유한 이메일 주소
@Column(updatable = false)
private Date createdAt; // 업데이트되지 않음
@Column(insertable = false)
private Date updatedAt; // 삽입 시에 값 지정 불필요
@Column(columnDefinition = "VARCHAR(255) DEFAULT 'N'")
private String status; // 컬럼 정의 직접 지정
@Column(table = "user_profile")
private String profileImage; // "user_profile" 테이블의 컬럼과 매핑
@Temporal(TemporalType.DATE)
@ColumnDefault("'9999-12-31'::DATE") // 기본값 9999-12-31, create 할 때 기본값 적용
pirvate Date testAt
@CreationTimestamp // Insert 쿼리가 발생하면 현재 시간을 적용, update 시에는 @UpdateTimestamp
@Column
pirvate Date testAt
6. @Temporal
JPA Entity Class에서 날짜와 시간 타입의 DB 컬럼을 매핑할 때 사용한다.
어떤 날짜/시간 타입으로 매핑할 지 정한다.
value | TemporalType.TIMESTAMP | 1. 날짜와 시간 정보 모두 매핑 |
value | TemporalType.DATE | 1. 날짜만 매핑, 시간 정보는 무시 |
# @Temporal 어노테이션은 주로 java.util.Date 또는 java.util.Calendar 타입의 필드와 함께 사용됩니다.
# Java 8부터는 java.time 패키지의 날짜/시간 타입(LocalDate, LocalDateTime, ZonedDateTime 등)과 함께 사용할 수도 있습니다.
# 이 경우에도 날짜/시간 유형에 맞는 @Temporal 값을 지정할 수 있습니다.
@Temporal(TemporalType.TIMESTAMP)
private Date timestampField;
@Temporal(TemporalType.DATE)
private LocalDate localDateField;
7. 관계 매핑
1). 관계
@ManyToOne | 다대일 ( N : 1 ) |
@OneToMany | 일대다 ( 1 : N ) |
@OneToOne | 일대일 ( 1 : 1 ) |
@ManyToMany | 다대다 ( N : N ) |
2). 공통 속성
targetEntity | 대상 엔티티 클래스를 지정 |
cascade | 1. 연관 엔티티와 관련된 동작 정의 ex)CasadeType.ALL=부모 엔티티 저장 시 자식 엔티티도 저장 |
fetch | 데이터를 가져온다. |
optional | 1. 관계 필드가 선택적인지 여부 2. true = 해당 필드는 NULL값을 가질 수 있다. |
2). 예제
(1). @ManyToOne - 단방향
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
===================================================================
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
}
# 단방향은 한 쪽의 엔티티가 상대 엔티티를 참조하고 있는 상태입니다.
# 그렇기 때문에 위와같이 Member 엔티티에만 @ManyToOne 어노테이션이 있다.
(2). @OneToMany - 양방향 **
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
Team은 Member를 List로 가지며, 연관관계의 주인을 정하기 위해 @OneToMany에 mappedBy 속성을 추가했습니다.
연관관계의 주인을 정하는 방법은 mappedBy 속성을 지정하는 것 입니다.
- 주인은 mappedBy 속성을 사용하지 않고, @JoinColumn을 사용
- 주인이 아닌 엔티티 클래스는 mappedBy 속성을 사용해 주인을 정할 수 있습니다.
주인은 mappedBy 속성을 사용할 수 없으므로 연관관계의 주인이 아닌 Team 엔티티에서 members 필드에
mappedBy의 속성으로 Member 테이블의 Team 필드 이름을 명시해줍니다.
(Team DB에 members FK 컬럼을 만들지 않게 하기 위해)
(3). @OneToOne
[단방향]
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long no;
@Column(nullable = false)
private String id;
@OneToOne
@JoinColumn(name = "blog_no")
private Blog blog;
}
=================================================================
public class Blog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long no;
@Column(nullable = false)
private String address;
}
[양방향]
public class Blog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long no;
@Column(nullable = false)
private String address;
@OneToOne(mappedBy = "blog")
private User user;
}
# 양방향이라서 주인을 정해야한다. User Class 코드는 동일
(4). @ManyToMany
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<Course> courses;
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "courses")
private List<Student> students;
}
8. @JoinColumn
@JoinColumn은 JPA 엔티티 클래스에서 조인 컬럼(FK)를 정의할 때 사용되며 주로 관계 매핑과 함께 사용된다.
name | 조인 컬럼의 이름 지정 |
referencedColumnName | 1. 연관 엔티티의 기본 키 컬럼 지정 2. 주로 @ManyToOne 관계에서 사용 |
nullable | 1. 컬럼이 NULL 값을 허용하는지 여부 지정 2. true로 설정하면 해당 컬럼은 NULL값을 가질 수 있다. |
unique | 1. 컬럼 값이 고유한지 여부 지정 2. true=컬럼 중복된 값 허용 X |
insertable | 1. 컬럼 값 삽입이 가능한지 여부 지정 2. false=삽입할 때 값을 지정 안해도 됨 |
updateable | 1. 컬럼 값 업데이트가 가능한지 여부 2. false=해당 컬럼 업데이트 X |
# name
@JoinColumn(name = "department_id")
private Long departmentId;
#referencedColumnName
@ManyToOne
@JoinColumn(name = "department_id", referencedColumnName = "id")
private Department department;
#nullable
@ManyToOne
@JoinColumn(name = "department_id", nullable = false)
private Department department; // NULL 값을 허용하지 않음
#unique
@OneToOne
@JoinColumn(name = "user_id", unique = true)
private User user; // 고유한 사용자 ID
#insertable
@ManyToOne
@JoinColumn(name = "department_id", insertable = false)
private Department department; // 삽입 시에 값 지정 불필요
#updatable
@OneToOne
@JoinColumn(name = "user_id", updatable = false)
private User user; // 업데이트되지 않음
8. @JoinTable
@JoinTable는 @ManyToMany일 때 사용한다. @JoinTable를 사용하면 연결 테이블(중간 테이블)을 정의한다.
name | 연결 테이블 이름 지정, 필수 !! |
joinColumns | 현재 엔티티(@JoinTable가 있는 엔티티)에서 사용하는 외래키 컬럼을 정의한다. |
inverseJoinColumns | 반대 엔티티에서 사용하는 외래 키 컬럼 정의 |
uniqueConstraints | 연결 테이블의 유니크 제약 조건 정의 배열 형태로 여러개 지정 가능 |
# name
@JoinTable(name = "student_course")
# joinColumns, inverseJoinColumns
@JoinTable(name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
# uniqueConstraints
@JoinTable(name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"),
uniqueConstraints = @UniqueConstraint(columnNames = {"student_id", "course_id"}))
9. @Transient
@Transient는 DB와 매핑하지 않음은 나타낸다. 즉 @Transient가 지정된 필드는 DB 컬럼으로 생성되지 않는다.
해당 필드는 엔티티 객체의 일시적인 데이터나 계산된 값을 저장하는 데 사용된다.
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Transient
private BigDecimal discount;
// 생성자, getter 및 setter 메서드
}
10. @NamedQuery
@NamedQuery를 사용하면 엔티티 클래스에서 정적으로 쿼리를 정의할 수 있다.
장점 =
- 정적으로 정의한 쿼리는 컴파일 시간에 검증되므로 런타임 오류를 방지할 수 있습니다.
- 쿼리가 여러 곳에서 사용되는 경우 중복 코드를 제거하고 유지 관리를 단순화할 수 있습니다.
- 쿼리 이름을 사용하여 쿼리를 참조하면 가독성이 향상되며 재사용성이 높아집니다.
name | 정적 쿼리의 이름 지정, 필수 ! |
query | JPQL 쿼리를 지정하고 작업을 정의, 필수 ! |
hint | 쿼리 힌트를 지정, 쿼리 실행하는 동안에 DB에 특정 설정을 전달 |
lockMode | 쿼리 실행 시 엔티티에 대한 락 모드 지정 1. LockModeType.READ 2. LockModeType.WRITE |
resultClass | 쿼리 결과가 엔티티가 아닌 다른 클래스인 경우 결과 클래스 지정 |
resultSetMapping | 쿼리 결과를 매핑할 때 사용할 결과셋 매핑 지정 |
@Entity
@NamedQuery(name = "findAllStudents", query = "SELECT s FROM Student s")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
}
참고 :
https://dev-coco.tistory.com/106