JPA 스터디 7주차
- JPA
- 2022. 2. 26. 22:20
이번주에 진행할 주제는 값 타입이다.
이번장에서 가장 중요한 키워드는 '공유'라고 생각한다.
자바에는 수 많은 타입들이 존재한다.
기본형, 객체형 ..까지 다양한 타입들이 존재한다.
이들에 대해 자세하게 설명하고 싶지만
글이 길어질 수 도 있기 때문에 작성하지는 않을 예정이다.
아무튼
기본형 같은 경우는 공유가 되지 않는다.
int a = 10이고
int b = 20일때 b =a // a = 30를 했을 경우에는 어떻게 될까?
바로 b = 10이 되버린다. 이는 공유가 된것이 아니라 값 자체가 복사가 되서 발생한 결과다.
그러니까 b의 입장에서는 20이라는 값이 의미가 없어지고 a의 값이 복사가 되서 그 값을 이용한다.
다른 타입들은 어떨까?
이거는 약간의 실습이 필요 하다.
Box boxA = new Box();
boxA.setValue(5);
Box boxB = boxA;
System.out.println("A box value : " + boxA.getValue());
System.out.println("B box value : " + boxB.getValue());
boxB.setValue(10);
System.out.println("A box value : " + boxA.getValue());
System.out.println("B box value : " + boxB.getValue());
a의 값과 b의 값은 어떨까?
같을까 다를까 실행해보자.
A box value : 5
B box value : 5
A box value : 10
B box value : 10
놀랍게도 값이 똑같이 나왔다.
첫번째는 그렇다고 치지만 두 번째는 두 개의 클래스가 서로 공유했다고 밖에 말하지 못할것 같다.
그거 말고는 설명할 방법이 전혀 없을 것 같기 때문이다.
그렇다면 JPA에서는 어떤 타입을 사용할까?
사실 두 가지 모두 사용이 되어진다.
기본형이나 객체형이나
근데 기본형같은 경우는 공부했던 것에 해답이 존재한다.
그러니까 내가 말하고 싶은 건 객체형이다.
근데 이 객체형을 리스트 같은 컬랙션에 저장한다는 건 아니고
public class Box {
private int value;
private Box2 box;
}
요렇게 객체안에 객체를 넣는 뭐 그런 느낌이다.
여기에는 문제가 있다.
그건 이걸 엔티티로 만드는 경우 JPA입장에서는 1:1 혹은 다:1 관계로 이해를 할것이다.
하지만 나는 이곳은 엔티티로 만들고 싶지 않는 경우라면?
당연한 이야기겠지만, error가 발생 된다.
그러면 방법이 없는 걸까?
다행히도 JPA는 이러한 문제를 해결하기 위해 방법을 제공하고 있다.
바로 @Embeddable, @Embedded를 이용하면 된다. 이 어노테이션들을 이용해서 xToOne으로 하는게 아닌 순수한 객체로
이용한다는 것을 JPA한테 알려주는 목적을 가지고 있다.
저것들이 뭔지는 일단 보면은 바로 이해가 된다고 생각한다.
@Embeddable
public class PhoneNumber {
private String areaCode;
private String localNumber;
@ManyToOne PhoneServiceProvider provider;
}
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
@Embedded
private PhoneNumber phoneNumber;
}
이것만 보고 이해가 되지 않을 것 같아 그림을 그려서 확인해보자.
요런 시스템이다. 결국 phone number라는걸 Member뿐만 아니라 다른 곳에서도 사용이 가능하다.
Admin이 있다면 Admin에도 적용하고 ... 뭐 이런 느낌이다.
단순히 값만 존재하는 경우보다 훨씬 객체 지향처럼 만들 수 있다.
더 놀라운 사실은 이곳에서도 다:1같은 관계를 적용할 수 있다는 점이다.
속성을 재 정의하는 방법도 있지만, 그것은 굳이 작성할 필요가 없을 것 같아 생략한다.
근데 맨 처음에 이번장에서 가장 중요한 키워드는 '공유'라고 한적이 있다.
그 이유가 무엇일까?
A box value : 5
B box value : 5
A box value : 10
B box value : 10
이러한 현상이 DB에도 나타난다는 이야기인데
이렇게 되버리면 내가 수정할려고 안했는데 같은 객체를 쓴다는 이유 하나만으로 원치 않는 값이 들어 올 수 있다.
어떻게 하면 이것을 막을 수 있을까?
1. 클래스에 final을 붙여서 클래스가 변경이 되지 않도록 유도한다.
2. 같은 객체를 사용하지 않도록 항상 객체를 새로 만들어서 사용한다.
이 두 가지 규칙만 정해도 위와 같은 이상현상은 막을 수 있다.첫번째 같은 경우는 직접적으로 막는다기 보다는 객체가 수정되는것을 막기 위한 방법이라 생각하면 된다.또한 인간의 실수를 조금이라도 줄이는 방법이라 생각한다. 왜냐하면 객체를 수정하게 되면 컴파일에러가 발생하기 때문이다.
테스트를 조금 진행해봤는데 final을 왜 붙이지? 상속을 막기 위해서인가?
테스트안해봤으면 맞다고 생각할 뻔했네
Member memberA = new Member();
Address address = new Address();
address.setCity("서울");
address.setState("1-1");
address.setStreet("회안대로");
address.setZipCode(null);
memberA.setAddress(address);
em.persist(memberA);
address.setCity("경기도");
memberA.setAddress(address);
em.persist(memberA);
Member memberB = new Member();
Address addressB = address;
address.setCity("강원도");
memberB.setAddress(addressB);
em.persist(memberB);
tx.commit();
내가 이런식으로 저장했는데
결과는
ID COMPANY_CITY STATE STREET PLUSFOUR ZIP NAME AREACODE LOCALNUMBER PROVIDER_NAME
1 강원도 1-1 회안대로 null null null null null null
2 강원도 1-1 회안대로 null null null null null null
(2 행, 5 ms)
이렇게 나온다. 나는 memberB를 한번도 강원도로 바꾼적이 없다. 이러한 이유때문에
같은 객체를 사용을 금지하는거라 생각이 든다.
Member memberB = new Member();
Address addressB = new Address();
addressB.setCity("강원도");
addressB.setStreet("강남역 1번 출구");
memberB.setAddress(addressB);
em.persist(memberB);
addressB.setCity("경상도");
em.persist(memberB);
요걸 이렇게 변경하고 실행해보면
ID COMPANY_CITY STATE STREET PLUSFOUR ZIP NAME AREACODE LOCALNUMBER PROVIDER_NAME
1 경기도 1-1 회안대로 null null null null null null
2 경상도 null 강남역 1번 출구 null null null null null null
서로가 간섭하지 않고 객체가 변경이 된다는 것을 알 수 있다.
따라서 결론은
1. 객체지향처럼 엔티티를 구축하고 싶다면 @Embeddable, @Embedded를 활용하자.
2. 같은 엔티티를 2번이상 사용하지 말자.
'JPA' 카테고리의 다른 글
쿼리 메소드 기능(2) (0) | 2022.04.05 |
---|---|
쿼리 메소드 기능(1) (0) | 2022.04.01 |
JPA 스터디 6주차 (1) | 2022.02.20 |
JPA 스터디 5주차 (0) | 2022.02.11 |
JPA 스터디 4주차 (0) | 2022.02.02 |