[java] JPA Embeddable By starseat 2022-10-26 08:53:17 java/spring Post Tags # JPA Embeddable 아래와 같은 테이블 구조 중 `address1`, `address2`, `zipcode` 를 `Address` 라는 class 로 분리하고자 할 때 사용 ![image.png](/uploads/_temp/20221026/9fdccb00fb4c542534a6753b39058e0d.png) ## @Embeddable - Entity 가 아닌 타입을 한 개 이상의 필드와 매핑할 대 사용 - 예: Address, Money 등 묶을 수 있는 세부 속성 - Entity 의 한 속성으로 @Embeddable 적용 타입 사용 ```java @Embeddable public class Address { @Column(name = "addr1") private String address1; @Column(name = "addr2") private String address2; @Column(name = "zipcode") private String zipcode; ... } ``` ```java @Entity @Table(name = "user_info") public class User { @Id @Column(name = "user_id") private String id; ... @Embedded private Address address; } ``` ### 사용 예 #### 생성 ```java tx.begin(); Address address = new Address("주소 1", "주소 2", "12345"); User user = new User("10001", ..., address); em.persist(user); tx.commit(); ``` - Address 가 `null` 일 때는 해당 값들이 `DB` 에 `null` 로 들어감 ```java tx.begin(); Address address = null; User user = new User("10002", ..., address); em.persist(user); tx.commit(); ``` #### 조회 ```java User user = em.find(User.class, "10001"); logger.info("주소: {}", user.getAddress()); ``` - 조회도 `Address` 가 `null` 일 경우 `Address` 객체가 `null` 임. ``` User user = em.find(User.class, "10002"); boolean isAddr = user.getAddress() == null; // true ``` ## 같은 @Embeddable 타입 필드가 두개면? ```java @Entity public class Employee { @Id private String id; @Embedded private Address homeAddress; @Embedded private Address workAddress; } ``` 위와 같은 경우라면 `org.hibernate.MappingException` 이 발생함. 이 경우는 `@AttributeOverride` 로 설정 재정의 해야함. ## @AttributeOverride 설정 재정의 ```java @Entity public class Employee { @Id private String id; @Embedded private Address homeAddress; @AttributeOverrides({ @AttributeOverride(name = "address1", column = @Column(name = "waddr1")), @AttributeOverride(name = "address2", column = @Column(name = "waddr2")), @AttributeOverride(name = "zipcode", column = @Column(name = "wzipcode")), }) @Embedded private Address workAddress; ... } ``` ## 다른 테이블에 값을 저장할 때 ![image.png](/uploads/_temp/20221026/e8b2098c41cf7281547278b047facc2c.png) ### 방법 1 : @SecondaryTable + 테이블 명 ```java @Embeddable public class Intro { @Column( table = "writer_intro", name = "content_type" ) private String contentType; @Column(table = "writer_intro") private String content; } ``` ```java @Entity @SecondaryTable( name = "writer_intro", pkJoinColumns = @PrimaryKeyJoinColumn( name = "writer_id", // writer_intro 테이블 컬럼 referencedColumnName = "id" // writer 테이블 컬럼 ) ) public class Writer { ... @Embedded private Intro intro; ... } ``` ### 방법 2 : @SecondaryTable + @AttributeOverride ```java @Embeddable public class Address { @Column(name = "addr1") private String address1; @Column(name = "addr2") private String address2; @Column(name = "zipcode") private String zipcode; ... } ``` ```java @Entity @SecondaryTables({ @SecondaryTables( name = "writer_intro", pkJoinColumns = @PrimaryKeyJoinColumn(name = "writer_id", referencedColumnName = "id") ), ... }) public class Writer { ... @Embedded @AttributeOverrides({ @AttributeOverride(name = "address1", column = @Column(table = "writer_address", name = "addr1")), @AttributeOverride(name = "address2", column = @Column(table = "writer_address", name = "addr2")), @AttributeOverride(name = "zipcode", column = @Column(table = "writer_address") }) private Address address; } ``` ### 사용 예 - 저장 ```java Writer w = new Writer( "name", new Address("주소 1", "주소 2", "12345"), new Intro("text/plain", "테스트") ); em.persist(w); ``` ```sql insert into writer (name) values (?) insert into writer_address (addr1, addr2, zipcode, writer_id) value (?, ?, ?, ?) insert into writer_intro (content, content_type, writer_id) value (?, ?, ?) ``` - 저장 2 : intro 값이 null 일 경우 ```java Writer w = new Writer( "name2", new Address("주소 1", "주소 2", "12345"), null ); em.persist(w); ``` ```sql insert into writer (name) values (?) insert into writer_address (addr1, addr2, zipcode, writer_id) value (?, ?, ?, ?) ``` - 조회 : `left join` 사용 - `inner join` 을 사용할 경우 @Embeddable 항목이 없을 경우 조회가 안됨. ```java Writer writer = em.find(Writer.class, id); ``` ```sql select w1_0.id,w1_0,name,w1_1.addr1,w1_1.addr2,w1_1.zipcode,w1_2.content,w1_2.content_type from writer w1_0 left join writer_address w1_1 on w1_0.id=w1_1.writer_id left join writer_intro w1_2 on w1_0.id=w2_2.writer_id where w1_0.id=? ``` - 변경 1 : Address 에 값 지정 ```java Writer writer = em.find(Writer.class, id); writer.setAddress(new Address("새 주소 1", "새 주소 2", "12345")); ``` ```sql -- address 가 null 이 아닌 값이 존재할 경우 update writer_address set addr1=?, addr2=?, zipcode=? where writer_id? -- address 가 null 이었을 경우 insert into writer_address (addr1, addr2, zipcode, writer_id) values (?, ?, ?, ?) ``` - 변경 2 : Address 에 null 지정 ```java Writer writer = em.find(Writer.class, id); writer.setAddress(null); ``` ```sql -- address 가 null 이 아닌 값이 존재할 경우 delete from writer_address where writer_id=? ``` - 삭제 ```java Writer writer = em.find(Writer.class, id); em.remove(writer); ``` ```sql delete from writer_intro where writer_id=? delete from writer_address where writer_id=? delete from writer where id=? ``` # 출처 - [최범균님의 JPA 기초 06 @Embeddable](https://www.youtube.com/watch?v=WtS5IszIueA) - [최범균님의 JPA 기초 07 @Embeddable2](https://www.youtube.com/watch?v=3_sdQGfL2Lg) Previous Post [java] JPA 식별자 생성 방식 Next Post [java] JPA 값 콜렉션 매핑