프로젝트 세팅
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="javax.persistence.jdbc.user" value="springjpa" />
<property name="javax.persistence.jdbc.password" value="springjpa" />
<property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<!-- <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL10Dialect" /> -->
<!-- 테이블 생성 옵션 -->
<property name="hibernate.hbm2ddl.auto" value="create"/>
<!-- 옵션 -->
<!-- 콘솔에 하이버네이트가 실행하는 SQL문 출력 -->
<property name="hibernate.show_sql" value="true" />
<!-- SQL 출력 시 보기 쉽게 정렬 -->
<property name="hibernate.format_sql" value="true" />
<!-- 쿼리 출력 시 주석(comments)도 함께 출력 -->
<property name="hibernate.use_sql_comments" value="true" />
</properties>
</persistence-unit>
</persistence>
이론
- 임베디드 타입 ( 복합 값 타입 )
- 내장 타입
- 새로운 값 타입을 직접 정의 할 수 있음
- JPA는 임베디드 타입 ( embedded type ) 이라고 한다
- 주로 기본값 타입을 모아 만들어서 복합값 타입이라고도 한다
- int, String과 같은 값 타입
- JPA에서 임베디드 타입 사용법
- @Embeddable : 값 타입을 정의하는 곳에 표시
- @Embedded : 값 타입을 사용하는 곳에 표시
- 기본 생성자 필수
- 임베디드 타입의 장점
- 재사용
- 높은 응집도
- 객체지향적인 설계 가능하다.
- 임베디드 타입과 테이블 매핑
- 임베디드 타입은 엔티티의 값일 뿐이다.
- 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다.
- 잘 설계된 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다.
시작
Member.java
생성@Entity
@TableGenerator(name = "MEMBER_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "MEMBER_SEQ", allocationSize = 1)
@Getter @Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "MEMBER_SEQ_GENERATOR")
private Long id;
@Column(name = "name", nullable = false)
private String username;
private LocalDateTime startDate;
private LocalDateTime endDate;
private String city;
private String street;
private String zipcode;
}
JpaMain.java
생성public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
emf.close();
}
}
}
Period.java
생성@Embeddable
@Getter @Setter
public class Period {
private LocalDateTime startDate;
private LocalDateTime endDate;
}
// 테스트를 위한 파라미터가 있는 생성자
public Period(LocalDateTime startDate, LocalDateTime endDate) {
super();
this.startDate = startDate;
this.endDate = endDate;
}
// 기본생성자는 반드시 있어야 한다.
public Period() {}
Address.java
생성@Embeddable
@Getter @Setter
public class Address {
private String city;
private String street;
private String zipcode;
}
public Address(String city, String street, String zipcode) {
super();
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
public Address() {}
Member.java
수정...
// private LocalDateTime startDate;
// private LocalDateTime endDate;
// period
// @Embedded 와 @Embeddable 둘중에 하나만 넣어도 되지만 둘다 넣어줄 것을 권장
@Embedded
private Period period;
// private String city;
// private String street;
// private String zipcode;
// Address
@Embedded
private Address address;
...
JpaMain.java
...
try {
Member member = new Member();
member.setUsername("user");
member.setAddress(new Address("서울", "역삼", "123"));
member.setPeriod(new Period(LocalDateTime.now(), LocalDateTime.now()));
tx.commit();
} catch (Exception e) {
...
결과
@AttributeOverride : 속성 재정의
- 한 엔티티에서 같은 값 타입을 사용하면 -> 컬럼명이 중복됨
- @AttributeOverrides, @AttributeOverride 를 사용해서 컬럼 명 속성을 재정의
Member.java
수정...
// 주소
@Embedded
private Address address;
// 회사 주소
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "WORK_CITY")),
@AttributeOverride(name = "street", column = @Column(name = "WORK_STREET")),
@AttributeOverride(name = "zipcode", column = @Column(name = "WORK_ZIPCODE"))
})
private Address workAddress;
...
- 불변객체
- 객체 타입을 수정 할 수 없게 만들면 부작용을 원천 차단
- 값 타입은 불변 객체로 설계해야함
- 생성 시점 이후에 절대 값을 변경 할 수 없는 객체
- 생성자로만 값을 설정하고 수정자( setter) 를 만들지 않으면 됨
( 바로 위 Member.java 수정 부분 주석처리 후 )
JpaMain.java
수정...
try {
// Member member = new Member();
// member.setUsername("user");
// member.setAddress(new Address("서울", "역삼", "123"));
// member.setPeriod(new Period(LocalDateTime.now(), LocalDateTime.now()));
//
// em.persist(member);
Address addr = new Address("서울", "역삼", "123");
Member member = new Member();
member.setUsername("user1");
member.setAddress(addr);
em.persist(member);
Member member2 = new Member();
member2.setUsername("user2");
// user1과 user2가 같은 addr을 가지고 있다.
member2.setAddress(addr);
em.persist(member2);
// user1의 주소만 newCity로 변경하고 싶다.
member.getAddress().setCity("newCity");
tx.commit();
...
-> uesr1의 주소만 바꾸고싶었지만 둘다 바뀐것이 확인된다.
>> 두 user 모두 하나의 addr 객체를 바라보고 있었기 때문
>>
JpaMain.java
수정...
Address copyAddr = new Address(addr.getCity(), addr.getStreet(), addr.getZipcode());
Member member2 = new Member();
member2.setUsername("user2");
// user1과 user2가 같은 addr을 가지고 있다.
member2.setAddress(copyAddr);
em.persist(member2);
...
JpaMain.java
수정
...
try {
Address addr = new Address("서울", "역삼", "123");
Member member = new Member();
member.setUsername("user1");
member.setAddress(addr);
em.persist(member);
Member member2 = new Member();
member2.setUsername("user2");
member2.setAddress(addr);
em.persist(member2);
// user1의 주소만 newCity로 변경하고 싶다.
Address newAddr = new Address("newCity", "역삼", "123");
member.setAddress(newAddr);
em.persist(newAddr);
tx.commit();
...
Address.java
수정
@Embeddable
@Getter
public class Address {
...