노트코드
노트코드
노트코드

블로그 메뉴

  • 홈
  • 이력서
  • 이전 블로그
  • 글쓰기
  • 관리자페이지
  • 분류 전체보기 (57)
    • 코틀린 (2)
      • 실무 프로젝트로 배우는 Kotlin & Sprin.. (2)
    • JAVA (1)
      • 디자인패턴 (1)
      • 객체지향 5대원칙 (0)
    • SPRING (32)
      • JPA (11)
      • 스프링시큐리티 (1)
      • 스프링 (8)
      • QueryDsl (1)
      • 스프링배치 (11)
    • AZURE (0)
    • ETC (10)
      • MAVEN (0)
      • GIT (0)
      • ReMind (3)
      • Exception (1)
      • CS (6)
    • 책 (8)
      • 이것이 자바다 (8)

최근 글

최근 댓글

태그

  • JPA
  • 스프링
전체 방문자
오늘
어제
hELLO · Designed By 정상우.
노트코드

노트코드

SPRING/JPA

[JPA] 값 타입종류

2022. 1. 21. 01:25

엔티티 타입

  1. @Entity로 정의하는 객체
  2. 데이터가 변해도 식별자로 지속해서 추적 가능

값 타입

  1. int, integer, String처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
  2. 식별자가 없고, 값만 있으므로 변경시 추적 불가

값타입의 분류

  • 기본값 타입2.값을 공유하면 안됨
    • int, double 같은 기본 타입은 절대 공유 x
    • Integer같은 래퍼 클래스나 String같은 특수 클래스는 공유 가능한 객체이지만 변경이 안됨
    3.자바 기본 타입(int, double)5.String
  • 4.래퍼 클래스(Integer, Long)
  • 1.(생명주기를 엔티티의 의존 → 회원을 삭제하면 이름, 나이 필드 함께 삭제)
  • 임베디드 타입(예를 들면 x,y값을 같이 묶어서 사용하고 싶을때)
    1. 새로운 값 타입을 직접 정의할 수 있음
    2. JPA는 임베디드 타입이라 함
    3. 주로 기본 값 타입을 모아 만들어서 복합 값 타입이라고도 함
    4. int, String과 같은 값 타입
    <특징>
    1. 임베디드 타입은 엔티티의 값일 뿐이다.
    2. 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다.
    3. 객체와 테이블을 아주 세밀하게 매핑하는 것이 가능
    4. 잘 설계한 ORM애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많음
    <사용법>
    1. 기본 생성자는 필수
    2. @Embeddable
    3. 값 타입을 정의 하는곳에 표시
    4. @Embedded
    5. 값 타입을 사용하는 곳에 표시한다.
    <aside> 💡 @Embedable과 Embedded중 하나만 명시하면 되지만, 둘 다 적는 것을 권장한다.임베디드 타입을 포함한 모든 값 타입은 엔티티의 생명주기에 의존하므로 엔티티와 임베디드 타입의 관계를 UML로 표현하면 컴포지션 관계가 된다.아래와 같은 코드 일때 같은 Address의 사용으로 에러가 발생아래와 같이 수정 해줘야 함
    public class Mebmer{
    	//기타 객체
    	@Embedded
    	private Address homeAddress;
    	@Embedded
    	@AttributeOverrides({
    				@AttributeOverride(name="city", column=@Column(name ="WORK_CITY")),
    				@AttributeOverride(name="street", column=@Column(name ="WORK_ST")),
    				@AttributeOverride(name="zipcode", column=@Column(name = "WORK_ZP"))
    	})
    	private Address workAddress
    }
    
    @Embeddable
    public class Address{
    	private String city;
    	private String street;
    	private String zipcode;
    }
    
    값 타입과 불변 객체
    1. 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함
    2. 부작용(side effect)발생
    3. 객체의 공유 참조는 피할 수 없음. 따라서 근복적인 해결책이 필요한데 가장 단순한 방법은 객체의 값을 수정하지 못하게 막으면 된다. 예를들면 Setter같은 수정자 메소드를 모두 제거한다. 그러면 공유 참조를 해도 값을 변경하지 못하므로 부작용을 막을 수 있다.
    Address address = new Address("CITY","street","10000");
    
    Member member = new Member();
    member.setUsername("member1")
    member.setHomeAddress(address);
    em.persist(member1);
    
    Member member2 = new Member();
    member2.setUsername("member2")
    member2.setHomeAddress(address);
    
    em.persist(member2);
    
    //첫 번째 member의 Address(city) 속성만 변경하고 싶다.
    member.getHomeAddress().setCity("new city");
    tx.commit();
    //member1과 member2 두 곳에서 update가 발생
    
    
    <aside> 💡 만약 member의 속성 값만 바꾸고 싶다면? 또는 현재 값을 복사하고 싶다면? 아래와 같이 새로운 객체를 만들어서 저장
    Address address = new Address("city", "street", "10000");
      // 
      Member member = new Member();
      member.setUsername("member1");
      member.setHomeAddress(address);
      em.persist(member);
      // Address 객체 복사
      Address copyAddress = new Address(address.getCity(), address.getStreet(), address.getZipcode());
      // 
      Member member2 = new Member();
      member2.setUsername("member2");
      member2.setHomeAddress(copyAddress); // 복사한 것을 넣는다.
      em.persist(member2);
      // 첫 번째 member의 Address(city) 속성만 변경된다.!
      member.getHomeAddress().setCity("new city");
    
    즉 위와 같은 문제를 막기 위해서는 불변 객체를 사용해야 한다.
    1. 값 타입은 불변 객체로 설계해야 한다.
      • 객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단할 수 있다.
      • 불변이라는 작은 제약으로 부작용이라는 큰 재앙을 막을 수 있음
    2. 불변 객체란
      • 생성 시점 이후 절대 값을 변경할 수 없는 객체
      • 생성자로만 값을 설정하고 Setter를 만들지 않으면 된다.
    3. 값을 변경하고 싶으면? 아래와 같이 생성자를 통해 새로운 객체를 만들고 통으로 변경함
    //Address를 불변 객체로 만들어보기
    @Embeddable
    public class Address{
    	private String city;
    	
    	protected Address(){} // JPA에서 기본 생성자는 필수다.
    	
    	//생성자로 초기 값을 설정한다.
    	public Address(String city){
    		this.city = city
    	}
    
    	public String getCity(){
    		return city;
    	}
    
    	//수정자는 만들지 않는다.
    }
    
    Address address = new Address("city", "street", "10000");
        
      Member member = new Member();
      member.setUsername("member1");
      member.setHomeAddress(address);
      em.persist(member);
        
      Address newAddress = new Address("NewCity", address.getStreet(), address.getZipcode());
      member.setHomeAddress(newAddress); // 통으로 변경 
        
      tx.commit();
    <https://gmlwjd9405.github.io/2019/09/12/value-type-of-basic-and-embedded.html값> 타입의 비교
    
  • 불변 객체
  • </aside>
  • 값 타입 공유 참조
  • 코드가 실행하면 workAddress의 임베디드 부분은 WORK_~~~필드로 만들어지게 됨
  • public class Mebmer{ //기타 객체 @Embedded private Address homeAddress; @Embedded private Address workAddress } public class Address{ private String city; private String street; private String zipcode; }
  • @AttributeOverride: 속성 재정의
  • </aside>
  • 값 타입의 비교
    1. 동일성 비교: 인스턴스의 참조 값을 비교,== 사용
    2. 동등성 비교: 인스턴스의 값을 비교, equals()사용
    만약 값을 비교 하고 싶다면 등동성 비교인 equals를 사용해야 하고,값타입의 equals()를 재정의 할 때는 모든 필드의 값을 비교하도록 구현해야 한다.
  • 자바가 제공하는 객체의 비교는 2가지이다.
  • 컬렉션 값 타입
    1. 값 타입을 컬렉션에 담아서 쓰는 것을 말한다.
    2. 값 타입 컬렉션은 값 타입을 하나 이상 저장할 때 사용
    3. 데이터 베이스에는 List나, Set과 같은 컬렉션의 형태가 없기 때문에, 별도의 테이블에 저장
    4. 컬렉션은 1:N의 개념이다.
public class Member {
    @Id
    private Long id;
    
    private Set<String> favoriteFoods;
    private List<Address> addressHistory;
}

구현 예시

기본 어노테이션 @ElementCollection, @CollectionTable을 사용

@Entity
public class Member {
    ...
    @ElementCollection
    @CollectionTable(
        name = "FAVORITE_FOOD",
        joinColumns = @JoinColumn(name = "MEMBER_ID"))
    @Column(name = "FOOD_NAME") // 컬럼명 지정 (예외)
    private Set<String> favoriteFoods = new HashSet<>();

    @ElementCollection
    @CollectionTable(
        name = "ADDRESS",
        joinColumns = @JoinColumn(name = "MEMBER_ID"))
    private List<Address> addressHistory = new ArrayList<>();
    ...
}

@Embeddable
public class Address{
	@Column
	private String city;
	private String street;
	private String zipcode;
}

값 타입 컬렉션도 마찬가지로, 값을 수정하기 위해서는 새로운 인스턴스를 생성하여 통으로 갈아 끼워야한다.

값 타입 컬렉션의 제약사항

  • 값 타입 컬렉션은 값 타입 엔티티와는 다르게 식별자 개념이 없다.
  • 값이 변경은 되지만 추적이 어렵다.ADDRESS에는 ID가 존재하지 않는다. 그렇기 때문에 값이 중간에 변경되었을 때 DB가 해당 ROW만을 찾아서 변경이 어렵다.
    • 대안
      • @orderColumn(name="address_history_order")를 사용하여 update query가 동작하게 할 수 있으나, 컬럼을 하나 더 만들어 사용자가 아닌 jpa가관리하게 만드는 방법이다.그렇다 보니 의도한 대로 동작하지 않을때가 많다.
    • 결론
      • 값 타입 컬렉션을 사용하지 말고, 엔티티타입을 사용하는것이 좋다(엔티티를 새로 만들어 @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)관계를 고려하는것이 좋음)→ 값타입을 엔티티로 승격시키는것
      • 값 타입 컬렉션은 정말 단순한 경우에 사용하자..[치킨, 피자,족발]등과 같은 메뉴 선택박스값으로 순서가 섞여도 상관없는 대상...
  • 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다
  • Hibernate: create table ADDRESS ( MEMBER_ID bigint not null, city varchar(255), street varchar(255), zipcode varchar(255) )

'SPRING > JPA' 카테고리의 다른 글

[JPA] 프록시  (0) 2022.01.21
[JPA]객체지향 쿼리 심화  (0) 2022.01.21
[JPA]NamedQuery  (0) 2022.01.21
[JPA] 엔티티 직접사용  (0) 2022.01.21
[JPA] Hibernate 초기화 전략  (0) 2022.01.21
    'SPRING/JPA' 카테고리의 다른 글
    • [JPA]객체지향 쿼리 심화
    • [JPA]NamedQuery
    • [JPA] 엔티티 직접사용
    • [JPA] Hibernate 초기화 전략
    노트코드
    노트코드
    노션 블로그에서 티스토리로 이전공사중

    티스토리툴바