import java.beans.Transient;
@Entity
public class Member {
@Id
private Long id;
@Column(name = "name") // 해당 이름으로 칼럼 매핑
private String username;
private Integer age; // 생각한대로 매핑됨
@Enumerated(EnumType.STRING) // enum 타입 지정
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP) //날짜 매핑(TIMESTAMP : 날짜+시간)
private Date createdDate;
@Lob // 큰 컨텐츠(BLOB, CLOB 등)
private String description;
@Transient // 매핑에 사용 X
private int temp;
}
1) @Id
기본 키(PK)를 매핑한다
2) @Column
필드와 컬럼을 매핑한다
설정
name : 필드와 매핑할 테이블의 컬럼 이름
insertable : 등록가능 여부 지정
updatable : 변경가능 여부 지정
nullable : null 값 허용 여부 설정
unique : 유니크 제약조건 걸 때 사용(잘 안씀) -> @Table에서 검
columnDefinition : DB 칼럼정보를 직접 줌
length : 문자 길이 제약조건
precision, scale : BigDecimal(큰 숫자, 소수점 등) 타입에서 사용
제약조건 추가
@Column(nullable = false, length = 10)
3)@Enumerated
EnumType.STRING을 사용하자!
enum 이름을 DB에 저장
EnumType.ORDINAL 사용 X
4) @Temporal
날짜 타입(Date, Calendar 유틸) 매핑
LocalDate, LocalDateTime을 사용할 때는 생략 가능
5) @Lob
DB BLOB(문자X), CLOB(문자) 타입과 매핑
6) @Transient
필드 매핑X
7) @ManyToOne, @JoinColumn
연관관계를 매핑한다
1대1, m대n 등
4-4 기본키(PK) 매핑
1) @Id
직접 할당
2) @GeneratedValue
자동 생성
AUTO : DB 방언에 따라 자동 지정(기본값)
IDENTITY : DB에 위임, MYSQL
SEQUENCE : DB 시퀀스 오브젝트 사용, ORACLE
TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용, 성능저하 우려 있고 잘 안쓴다
3) IDENTITY 전략
기본 키 생성을 데이터베이스에 위임
class Member {
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer id;
}
MySQL의 AUTO_ INCREMENT 등에서 사용
AUTO_ INCREMENT : DB에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있음
SELECT 문 없이 ID 값이 객체에 자동 매핑됨
4) SEQUENCE 전략
class Member {
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@SequenceGenerator(
name = "MEMBER_SEQ_GENERATOR",
sequenceName = "MEMBER_SEQ", // 매핑할 데이터베이스 시퀀스 이름
initialValue = 1, allocationSize = 50)
public Integer id;
}
@Entity
public class Member {
private Long id;
@Column(name = "USERNAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamId;
}
@Entity
public class Team {
@Id
@GeneratedValue
private Long id;
private String name;
}
//조회
Member findMember = em.find(Member.class, member.getId());
//연관관계가 없음
Team findTeam = em.find(Team.class, team.getId());
테이블 : 외래키 조인 vs 객체 : 참조
객체를 테이블에 맞춰 모델링
식별자로 다시 조회해야 한다
5-3 단방향 연관관계
@Entity
public class Member {
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
public class MainJPA {
public static void main(String[] args) {
//팀 저장
Team team = new Team("TeamA");
em.persist(team);
//회원 저장
Member member = new Member("member1", team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
// 새로운 팀B
Team teamB = new Team("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
}
}
5-4 양방향 연관관계
@Entity
public class Member {
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
@Entity
public class Team {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
}
5-5 연관관계의 주인과 mappedBy
@OneToMany(mappedBy = "team")
객체 : 사실 2개의 단방향 연관관계
테이블 : PK, FK를 통해 양방향 연관관계
1) 딜레마~!
Member 객체 자체와 테이블 매핑 vs Team 속 members 리스트를 통해 테이블 매핑
만약 Member 객체는 그대로 인데 members 리스트는 빈 리스트이면???
둘중 뭐로 매핑할지 정해야 함
2) 연관관계의 주인(Owner)
위의 두 관계 중 하나만 연관관계 주인으로 설정
a. 주인
외래키가 있는 쪽이 주인!!!(team)
1대n 관계에서 n 쪽
외래키 관리(등록, 수정)
mappedBy 속성 사용X
b. 비 주인
주인의 반대편, PK 있는 쪽(members)
1대n 관계에서 1 쪽
수정 불가, 읽는 것만 가능
mappedBy 속성 사용!
@OneToMany(mappedBy = "team")
5-6 자주 하는 실수
1) 양방향 객체 설정 필수
class Main {
public static void main(String[] args) {
Team team = new Team("TeamA");
em.persist(team);
// 1. 역방향(주인이 아닌 방향)만 연관관계 설정
Member member = new Member("member1");
team.getMembers().add(member);
em.persist(member);
// 오류!!!
// 2. 연관관계 주인 방향도 값 설정
Member member = new Member("member1", team);//!!!!!!
team.getMembers().add(member);
em.persist(member);
// 정상 작동!!!
}
}
주인값, 비 주인값(리스트) 꼭 입력해야 함
순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자!
2) 연관관계 편의 메서드
@Entity
public class Member {
//...
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
public void changeTeam(Team team) {
this.team = team;
team.getMembers().add(this);// !!!!
}
}
섹션 4 : 엔티티 매핑
4-1 객체와 테이블 매핑
객체와 테이블을 매핑한다
1)
@Entity
final
,enum
,interface
,inner class
사용불가final
금지(바뀔 값이니까)name
속성 : JPA에서 쓸 엔티티 이름 지정(기본값 클래스 이름을 되도록 쓰자)2)
@Table
name
속성 : 매핑할 이름 지정(기본값 엔티티 이름)4-2 데이터베이스 스키마 자동 생성
hibernate.hbm2ddl.auto=CREATE
1) DDL
DDL
:Data definition language
DB
를 정의하는 명령어CREATE
,DROP
,ALTER
,TRUNCATE
등2) DDL 자동생성 기능
JPA
실행 로직에 영향 X3) 속성
create
: 기존테이블 삭제 후 재생성(기존 테스트코드에서 쓰던 방식)create-drop
: 종료 시점에 테이블 dropupdate
: 변경된 부분만 반영(운영 DB 사용 X : 테이블에 락 걸림)validate
: 엔티티와 테이블의 정상 매핑여부만 확인none
: 사용안함4) 주의사항
create
/update
update
/validate
(여기서도 가급적 쓰지 마라)validate
/none
4-3 필드와 컬럼 매핑
1)
@Id
2)
@Column
설정
name
: 필드와 매핑할 테이블의 컬럼 이름insertable
: 등록가능 여부 지정updatable
: 변경가능 여부 지정nullable
: null 값 허용 여부 설정unique
: 유니크 제약조건 걸 때 사용(잘 안씀) -> @Table에서 검columnDefinition
: DB 칼럼정보를 직접 줌length
: 문자 길이 제약조건precision
,scale
:BigDecimal
(큰 숫자, 소수점 등) 타입에서 사용제약조건 추가
@Column(nullable = false, length = 10)
3)
@Enumerated
enum
이름을 DB에 저장EnumType.ORDINAL
사용 X4)
@Temporal
날짜 타입(
Date
,Calendar
유틸) 매핑5)
@Lob
DB BLOB(문자X), CLOB(문자) 타입과 매핑
6)
@Transient
필드 매핑X
7)
@ManyToOne
,@JoinColumn
연관관계를 매핑한다
1대1
,m대n
등4-4 기본키(PK) 매핑
1)
@Id
2)
@GeneratedValue
AUTO
: DB 방언에 따라 자동 지정(기본값)IDENTITY
: DB에 위임,MYSQL
SEQUENCE
: DB 시퀀스 오브젝트 사용,ORACLE
TABLE
: 키 생성용 테이블 사용, 모든 DB에서 사용, 성능저하 우려 있고 잘 안쓴다3) IDENTITY 전략
MySQL의 AUTO_ INCREMENT 등에서 사용
AUTO_ INCREMENT
: DB에INSERT SQL
을 실행한 이후에 ID 값을 알 수 있음4) SEQUENCE 전략
allocationSize
: 한번에 50개의 인덱스 가져옴(테이블 전략에서도 사용)5) 권장하는 식별자 전략
PK
제약조건 : null 아님, 유일, 불변섹션 5 : 연관관계 매핑 기초
5-1 용어
Direction
): 단방향, 양방향Multiplicity
): 다대일(N:1
), 일대다(1:N
), 일대일(1:1
), 다대다(N:M
)Owner
): 객체 양방향 연관관계는 관리 주인이 필요5-2 연관관계가 필요한 이유
테이블 : 외래키 조인
vs객체 : 참조
5-3 단방향 연관관계
5-4 양방향 연관관계
5-5 연관관계의 주인과 mappedBy
1) 딜레마~!
Member
객체는 그대로 인데members
리스트는 빈 리스트이면???2) 연관관계의 주인(
Owner
)위의 두 관계 중 하나만 연관관계 주인으로 설정
a. 주인
b. 비 주인
5-6 자주 하는 실수
1) 양방향 객체 설정 필수
2) 연관관계 편의 메서드
Team.addMembers(team)
형태로 만들기도 함3) 무한루프
a.
toString()
member{team{member{team{...}}}}
lombok
에서는toString()
쓰지마라b. 이외
entity
를 반환 X,DTO
사용