일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Thymeleaf
- JPQL
- Servlet
- Proxy
- jpa
- JDBC
- springdatajpa
- kotlin
- Android
- db
- Spring Boot
- 스프링 핵심 기능
- QueryDSL
- 스프링
- http
- java
- transaction
- 백준
- Greedy
- AOP
- 김영한
- 그리디
- 스프링 핵심 원리
- pointcut
- spring
- 자바
- 인프런
- SpringBoot
- Exception
- 알고리즘
- Today
- Total
개발자되기 프로젝트
Embedded 본문
Embedded 타입이란? 값 타입의 일종으로 복합 값 타입으로 이해하면 된다.
여러개의 값을 모아서 하나의 값으로 사용함.
- ex) (x, y) -> point
값 타입이란 값 타입은 int, Integer, String과 같이 값을 가지고 있는 타입들이 속한다.
Embedded type은 여러개의 값을 묶어서 하나의 값으로 만들 수 있다.
embedded된 객체를 하나의 값으로 인식하자.
embedded된 객체를 활용하여 좀 더 객체 지향적, 깔끔한 코드를 작성이 가능하다.
1. embedded type없이
User class에 주소를 추해주자.
private String city;
private String district;
private String detail;
private String zipCode;
entity, db column이라고 해서...field를 막 선언하는게 맞나..?
*dry: don't repeat yourself에 위반된다. 코드는 복붙을 지양하고 객체화해서 사용해야 한다.
여기서 사용되는 개념이 embedded type이다.
2. embedded로 변환
주소를 객체화해서 사용하자.
<Address class> Embeddasble class로 명시하기 위해 @Embeddable annotation을 붙여준다.
@Embeddable
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String city; //시
private String district;//구
@Column(name = "address_detail")
private String detail; //상세주소
private String zipCode; //우편번호
}
그리고 address객체를 사용하는 User class에서는 Address객체에 @Embedded 명기해준다.
@Embedded
private Address address;
embedded된 객체를 사용해서 이전에 봤던 세부 사항들이 Address로 처리가 된다.
코드가 더욱 객체지향적, 깔끔해진다.
* @Embeddable : embedded화 할 class에 표시
* @Embedded : embedded된 객체를 사용하는 곳에 표시.
<Test>
@Test
void embeddedTest(){
userRepository.findAll().forEach(System.out::println);
User user = new User();
user.setName("hyun");
user.setAddress(new Address("서울시", "송파구", "송파대로", "0000"));
userRepository.save(user);
userRepository.findAll().forEach(System.out::println);
}
user table생성을 보면 주소에 관련된 columns이 생성되었고,
user 생성 후 입력한 주소가 잘 반영되어있다.
Hibernate:
create table user (
id bigint not null auto_increment,
created_at datetime(6) default now(6) comment '생성시간' not null,
updated_at datetime(6) default now(6) comment '수정시간' not null,
city varchar(255),
address_detail varchar(255),
district varchar(255),
zip_code varchar(255),
email varchar(255),
gender varchar(255),
name varchar(255),
primary key (id)
) engine=InnoDB
User(super=BaseEntity(createdAt=2021-07-10T19:12:41, updatedAt=2021-07-10T19:12:41),
name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null, address=null)
User(super=BaseEntity(createdAt=2021-07-10T19:12:41, updatedAt=2021-07-10T19:12:41),
name=park, email=park@google.com, id=2, testData=null, gender=null, address=null)
User(super=BaseEntity(createdAt=2021-07-10T19:12:41, updatedAt=2021-07-10T19:12:41),
name=lee, email=lee@naver.com, id=3, testData=null, gender=null, address=null)
User(super=BaseEntity(createdAt=2021-07-10T19:12:41, updatedAt=2021-07-10T19:12:41),
name=kim, email=kim@google.com, id=4, testData=null, gender=null, address=null)
User(super=BaseEntity(createdAt=2021-07-10T19:12:41, updatedAt=2021-07-10T19:12:41),
name=hyun, email=hyun@google.com, id=5, testData=null, gender=null, address=null)
User(super=BaseEntity(createdAt=2021-07-10T19:12:44.738212300, updatedAt=2021-07-10T19:12:44.738212300),
name=hyun, email=null, id=6, testData=null, gender=null, address=Address(city=서울시, district=송파구, detail=송파대로, zipCode=0000))
3. @AttributeOverride
Embeddable class를 한 객체에서 재활용 할 경우 table 생성 시 column명이 중복된다.
따라서 column명이 table에서 중복되지 않게 column 명 지정이 필요하다.
해당 기능을 제공하는 annotation이 @AttributeOverride이다.
@AttributeOverriedes의 하위에 @AttributeOverride의 배열로 넣어준다.
@AttibuteOvverides(
{
@AttributeOverride(name = "field - 1", column = @Column(name = "table -1")),
@AttributeOverride(name = "field - 2", column = @Column(name = "table -2")),
@AttributeOverride(name = "field - 3", column = @Column(name = "table -3"))
})
예를들어 user정보에 집주소, 직장주소 둘다 넣고싶어서 Address를 재활용했다고 해보자.
<user class>
@Embedded
private Address homeAddress;
@Embedded
private Address companyAdress;
하지만 이렇게되면 user table에 Address와 관련된 column이 2개 씩 존재하게된다.
기본적으로 DB에서 한 table에서 column명 중복을 허용하지 않는다.
@Embedded
@AttributeOverrides(
{@AttributeOverride(name = "city", column = @Column(name = "home_city")),
@AttributeOverride(name = "district", column = @Column(name = "home_district")),
@AttributeOverride(name = "detail", column = @Column(name = "home_address_detail")),
@AttributeOverride(name = "zipCode", column = @Column(name = "home_zip_code"))}
)
private Address homeAddress;
@Embedded
@AttributeOverrides(
{@AttributeOverride(name = "city", column = @Column(name = "company_city")),
@AttributeOverride(name = "district", column = @Column(name = "company_district")),
@AttributeOverride(name = "detail", column = @Column(name = "company_address_detail")),
@AttributeOverride(name = "zipCode", column = @Column(name = "company_zip_code"))}
)
private Address companyAdress;
table에 home address와 company address가 별도 column으로 생성되었고,
user entity에도 잘 입력되어있다.
Hibernate:
create table user (
id bigint not null auto_increment,
created_at datetime(6) default now(6) comment '생성시간' not null,
updated_at datetime(6) default now(6) comment '수정시간' not null,
company_city varchar(255),
company_address_detail varchar(255),
company_district varchar(255),
company_zip_code varchar(255),
email varchar(255),
gender varchar(255),
home_city varchar(255),
home_address_detail varchar(255),
home_district varchar(255),
home_zip_code varchar(255),
name varchar(255),
primary key (id)
) engine=InnoDB
User(super=BaseEntity(createdAt=2021-07-10T19:29:44.182866200, updatedAt=2021-07-10T19:29:44.182866200),
name=hyun, email=null, id=6, testData=null, gender=null,
homeAddress=Address(city=서울시, district=송파구, detail=송파대로, zipCode=0000),
companyAdress=Address(city=서울시, district=강남구, detail=강남대로, zipCode=0000))
4. Null이들어간다?
만약 Address에서 null point exception이 발생한다면 두 가지중 하나일 것이다.
setAddress(null) : null이 들어가거나 또는 setAddress(new Address()) : 빈 Address 객체가 들어가거나
<Test>
User user1 = new User();
user1.setName("bababa");
user1.setHomeAddress(null);
user1.setCompanyAdress(null);
userRepository.save(user1);
User user2 = new User();
user2.setName("AAAAA");
user2.setHomeAddress(new Address());
user2.setCompanyAdress(new Address());
userRepository.save(user2);
setAddress(null)인 경우는 Address조회시 null이 반환된다.
반면에 빈 Address객체의 경우 하위 각 항목이 null로 return된다.
User(super=BaseEntity(createdAt=2021-07-10T19:43:36.263778100, updatedAt=2021-07-10T19:43:36.263778100), name=bababa, email=null, id=7, testData=null, gender=null,
homeAddress=null,
companyAdress=null)
User(super=BaseEntity(createdAt=2021-07-10T19:43:36.275453800, updatedAt=2021-07-10T19:43:36.275453800), name=AAAAA, email=null, id=8, testData=null, gender=null,
homeAddress=Address(city=null, district=null, detail=null, zipCode=null),
companyAdress=Address(city=null, district=null, detail=null, zipCode=null))
native query를 통해 DB에 저장된 값을 확인해보자.
[7, 2021-07-10 19:48:53.331188, 2021-07-10 19:48:53.331188, null, null, null, null, null, null, null, null, null, null, bababa]
[8, 2021-07-10 19:48:53.339141, 2021-07-10 19:48:53.339141, null, null, null, null, null, null, null, null, null, null, AAAAA]
둘 다 address는 null인데 출력된 형식이 다르다.
왜 다를까? --> cache때문이다!
findAll()전 entitymanger를 clear하여 cache를 삭제해보자.
cache를 삭제했으니 DB에서 값을 읽어올 것이다.
짠 둘다 동일한 형식으로 출력되었다.
User(super=BaseEntity(createdAt=2021-07-10T19:51:43.602783, updatedAt=2021-07-10T19:51:43.602783), name=bababa, email=null, id=7, testData=null, gender=null,
homeAddress=null, companyAdress=null)
User(super=BaseEntity(createdAt=2021-07-10T19:51:43.610638, updatedAt=2021-07-10T19:51:43.610638), name=AAAAA, email=null, id=8, testData=null, gender=null,
homeAddress=null, companyAdress=null)
즉 embedded된 객체가 null인 경우 내부 column이 모두 null인 경우와 동일하다
5. GitHub : 210710 Imbedded Type