java 스프링부트 ( spring boot ) / JPA 임베디드 타입

김동명·2022년 12월 9일
0

스프링부트

목록 보기
16/19

프로젝트 세팅

  • persistence.xml
<?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 : 값 타입을 사용하는 곳에 표시
    - 기본 생성자 필수

  • 임베디드 타입의 장점
    - 재사용
    - 높은 응집도
    - 객체지향적인 설계 가능하다.

  • 임베디드 타입과 테이블 매핑
    - 임베디드 타입은 엔티티의 값일 뿐이다.
    - 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다.
    - 잘 설계된 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다.

시작

1. 임베디드 타입 준비

  • embedded 패키지 생성

  • 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();
		}
		
	}
	
}



2. 임베디드 타입 묶어주기

  • embedded 패키지 > 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() {}

  • embedded 패키지 > 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) {	
    ...

  • 결과




3. @AttributeOverride

@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;
	
...

  • 결과



4. 임베디드 타입 사용 시 주의사항 ( 객체 사용 유의 할 점 )

  • 불변객체
    - 객체 타입을 수정 할 수 없게 만들면 부작용을 원천 차단
    - 값 타입은 불변 객체로 설계해야함
    - 생성 시점 이후에 절대 값을 변경 할 수 없는 객체
    - 생성자로만 값을 설정하고 수정자( setter) 를 만들지 않으면 됨

( 바로 위 Member.java 수정 부분 주석처리 후 )


4-1. 같은 객체 사용 시


  • 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 객체를 바라보고 있었기 때문
>>




4-2. 해결 방법 ( 1 )

  • JpaMain.java 수정
    • Address copyAddr = new Address(addr.getCity(), addr.getStreet(), addr.getZipcode()); 로 객체를 추가
    ...
    			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);
    ...
  • 결과



4-2. 해결 방법 ( 2 )

  • JpaMain.java 수정

    • 새로운 Address 객체를 생성해서 넣어주기
    ...
    		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 수정

    • 객체의 @setter를 지워 불변객체로 만들어 준다.
      @Embeddable
      @Getter
      public class Address {
      ...
profile
코딩공부

0개의 댓글