[JPA] 복합 기본키

김정연·2024년 4월 28일
0
post-thumbnail

하나의 엔티티에 pk가 두개일 때는 복합 기본키로 설정을 해야한다.

엔티티에는 하나의 기본키만 존재하는데 예를 들어 회원과 상품을 한 테이블에 태우려면

private User user;
private Product product;

같이 두개의 기본키를 하나의 엔티티에 쓰는 경우가 있다.
그럴 때 사용하는 것이 복합 기본키이다. 여기서 중요한 것은 @Embeddable을 사용하려면 Serializable 인터페이스를 구현한 클래스를 꼭 선언해야한다.

직렬화(Serializable)란?

직렬화를 하는 이유는 java 객체를 JVM에서 꺼내서 파일과 같은 형태로 저장할 수 있는 것을 의미한다. 즉 Byte로 저장한 것을 다른 시스템에서도 가져와서 쓸 수 있게 만드는 것이다.

직렬화 예제 참고 링크!

복합키 설정 법

  • @Embeddable > 객체지향적
  • @IdClass

복합키 특징

  • 복합키는 별도의 식별자 클래스로 만들어야한다.
  • Serializable을 구현해야 한다.
  • equals와 hashCode 메소드를 구현해야한다.
  • 기본 생성자가 있어야 한다.
  • 식별자 클래스는 public 이어야 한다.

메서드 재정의(override)

package entity;

import java.io.Serializable;

public class MemberProductId implements Serializable {

    private String member;
    private String product;

    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }
}

여기서 member는 MEMBER객체 , product는 PRODUCT객체를 뜻한다. 그리고 Serializable를 쓰는 이유는 역직렬화, 직렬화 때문인데 객체의 내용이 같은지 비교해야하기 때문에 equals()와 hashcode()를 이용해 꼭 객체를 확인해줘야한다. 예를 들어 hashcode같은 경우는 같은 값을 담고 있어도 다른 객체로 간주될 수도 있다.

복합키는 항상 식별자 클래스를 만들어야하고, @IdClass , @EmbeddedId도 사용해야 한다...

IdClass

@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {

IdClass, EmbeddedId도 어노테이션은 복합키를 구성하는 여러개의 필드를 별도의 클래스에 분리하는 것이다.

하지만 어노테이션을쓰지 않고 단순하게 매핑해서 사용하는 방법도 있다(나는 이방법이 훨씬 좋다)

분리된 클래스

public class OrderMain {

    public static void main(String[] args) {

        // 엔티티 매니저 팩토리 생성
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa");
        // 엔티티 매니저 생성
        EntityManager em = emf.createEntityManager();
        // 트랜잭션 획득
        EntityTransaction tx = em.getTransaction();

        try{
            tx.begin();

            // 회원저장
            Member member1 = new Member();
            member1.setId("member1");
            member1.setUsername("회워1");
            em.persist(member1);

            // 상품저장
            Product productA = new Product();
            productA.setId("productA");
            productA.setName("상품1");
            em.persist(productA);

            // 주문저장
            Orders orders = new Orders();
            orders.setMember(member1);
            orders.setProduct(productA);
            orders.setOrderAmount(2);
            em.persist(orders);
            tx.commit();
        } catch (Exception e){
            tx.rollback();
        }

        Long orderId = 1L;
        Orders orders = em.find(Orders.class, orderId);
        Member member = orders.getMember();
        Product product = orders.getProduct();
        System.out.println(member.getUsername());
        System.out.println(orders.getMember().getUsername());
        System.out.println(orders.getOrderAmount());
    }
}

코드를 보면 회원 상품 주문을 각각 저장한 후 불러오고 있다. 여기서 주의해야 할 점은 Orders 클래스에 내가 객체로 저장했는지 아니면 pk로 저장했는지 여부를 따지고 값을 불러오는 것이다. 만약에 객체로 저장했다면 em.find로 객체 자체를 불러서 매핑해주어야한다. 실제 프로젝트를 진행하면서 객체와 Long 타입 두개로 받아봤는데 객체로 받으면 모든 정보를 불러올 수 있다는 장점이 있지만 필요없는 정보도 불러온다는 단점이 있다. 상황마다 다르게 사용하면 될 것 같다.

복합키 끝!

profile
백엔드 개발자

0개의 댓글