EnumType을 여러 개 받고 싶을 때

이진우·2023년 9월 19일
0

스프링 학습

목록 보기
14/41

Enum 타입을 여러개 가지고 싶을 때

한 Entity 가 여러 개의 Enum을 받고 싶을 때가 있다.
그럴때 유용하게 사용될 수 있는 방법이 있다.

일단 RoleType은

public enum RoleType {
    USER,ADMIN
}

로 가정한다.

방법 1

코드

 @ElementCollection(targetClass = RoleType.class)
    @JoinTable(name = "roles",joinColumns = @JoinColumn(name = "member_id"))
    @Column(name ="roleOfWeb", nullable = false) @Enumerated(EnumType.STRING)
    private Set<RoleType> roles=new HashSet<>();

1)@ElementCollection(targetClass = RoleType.class) :JPA 에서 컬렉션을 매핑할 때 사용, 열거형 RoleType이 타겟 클래스로 설정
2)@ElementCollection을 사용하여 열거형 값을 저장하는 데 필요한 테이블을 생성,그 이름은 roles 이다. 이따 DB를 보면 실제 roles 가 들어가 있는 것을 볼 수 있다.
3)(joinColumns=@JoinColumn(name="member_id") 생성한 테이블에 열을 추가하는데 joinColumn으로 지금 이 Entity와 연결하는 값을 가지고, Column으로는 "roleOfWeb"을 추가하여서 어떤 EnumType과 연관되는지 볼 수 있다.

그러면 아래 코드를 통해서 Enum Type을 넣을 수 있다.

 Member member=new Member();
              member.setName("member1");
              member.getRoles().add(RoleType.ADMIN);
              member.getRoles().add(RoleType.USER);
              //member.setTeam(team);
             em.persist(member);

방법 1 DB 결과

위 사진과 같이 잘 나타나는 것을 볼 수 있다.

방법 2

코드

@ElementCollection(fetch = FetchType.EAGER)
    @Enumerated(EnumType.STRING)
    private Set<RoleType> roles=new HashSet<>();

훨씬 간단한 방법같다.

이 방법으로 아래 코드를 실행 시키면

 Member member=new Member();
 member.setName("member1");
 member.getRoles().add(RoleType.ADMIN);
 member.getRoles().add(RoleType.USER);
 //member.setTeam(team);
 em.persist(member);

이렇게 결과가 실행 될 수 있다.

다만 이렇게 table 로 만들거면 Member 와 role을 1대 다 , 다대1로 쪼개는 것과 위 두 방식과의 차이점이 무엇인지는 조금 더 알아보아야 겠다.

출처:https://stackoverflow.com/questions/15998824/mapping-setenum-using-elementcollection

다만 나중에 조회할때 방법 2로 사용한 것은 Eager로 Member 를 조회하면 Join 으로 함께 항상 나타나는 반면 , 방법 1은 LAZY LOADING 으로 설정 되어 있다.

값 타입 컬렉션과의 비교 분석

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(city, address.city) && Objects.equals(street, address.street) && Objects.equals(zipcode, address.zipcode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(city, street, zipcode);
    }

    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }

    public Address() {
    }
}

이런 값타입을 Member 에서 Collection으로 받는다고 치자.

Member 클래스에

  @ElementCollection(targetClass = RoleType.class)
    @JoinTable(name = "roles",joinColumns = @JoinColumn(name = "member_id"))
    @Column(name ="roleOfWeb", nullable = false) @Enumerated(EnumType.STRING)
    private Set<RoleType> roles=new HashSet<>();

 @Embedded
    private Address address;

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

    public Set<RoleType> getSecondRoles() {
        return secondRoles;
    }

    public void setSecondRoles(Set<RoleType> secondRoles) {
        this.secondRoles = secondRoles;
    }

    @ElementCollection(fetch = FetchType.EAGER)
    @Enumerated(EnumType.STRING)
    private Set<RoleType> secondRoles=new HashSet<>();

를 추가하여 주고

	@Test
	@Transactional
	@Rollback(value = false)
	public void save(){
          Member member=new Member();
		  member.setName("jinu");
		  member.getRoles().add(RoleType.USER);
		  member.getRoles().add(RoleType.ADMIN);
		  member.getSecondRoles().add(RoleType.USER);
		  member.getSecondRoles().add(RoleType.ADMIN);

		  member.setAddress(new Address("도시","street","zipcode"));
		  member.getAddressHistory().add(new Address("안녕","스트릿","zip"));
		  member.getAddressHistory().add(new Address("안녕2","스트릿2","zip2"));

		  em.persist(member);
          em.flush();
		  em.clear();
		System.out.println("====================");
		Member findMember=em.find(Member.class,member.getId());
		findMember.getAddressHistory().remove(new Address("안녕","스트릿","zip"));
		findMember.getRoles().remove(RoleType.USER);
		findMember.getSecondRoles().remove(RoleType.USER);

	}

이런 테스트 코드를 작성해 보았다.

값타입 컬렉션을 쓰지 말아야 하는 가장 큰 이유는 값 타입 컬렉션의 일부를 지우더라도 모두 delete 해버리고 나중에 삭제 되지 않은 것을 다시 insert 쿼리가 날라가는게 문제였다.

그렇다면 ============= 이후에 쿼리를 관찰해서 모양과 형태가 비슷한 값타입이 아닌 EnumType도 일부만 삭제되었을때 다시 삭제되지 않은 것에 대한 insert 쿼리가 날라가는지 확인하자.

쿼리 결과

Hibernate: 
    select
        member0_.id as id1_1_0_,
        member0_.city as city2_1_0_,
        member0_.street as street3_1_0_,
        member0_.zipcode as zipcode4_1_0_,
        member0_.name as name5_1_0_,
        secondrole1_.member_id as member_i1_2_1_,
        secondrole1_.second_roles as second_r2_2_1_ 
    from
        member member0_ 
    left outer join
        member_second_roles secondrole1_ 
            on member0_.id=secondrole1_.member_id 
    where
        member0_.id=?
Hibernate: 
    select
        addresshis0_.member_id as member_i1_0_0_,
        addresshis0_.city as city2_0_0_,
        addresshis0_.street as street3_0_0_,
        addresshis0_.zipcode as zipcode4_0_0_ 
    from
        address addresshis0_ 
    where
        addresshis0_.member_id=?
Hibernate: 
    select
        roles0_.member_id as member_i1_3_0_,
        roles0_.role_of_web as role_of_2_3_0_ 
    from
        roles roles0_ 
    where
        roles0_.member_id=?
Hibernate: 
    delete 
    from
        address 
    where
        member_id=?
Hibernate: 
    insert 
    into
        address
        (member_id, city, street, zipcode) 
    values
        (?, ?, ?, ?)
Hibernate: 
    delete 
    from
        roles 
    where
        member_id=? 
        and role_of_web=?
Hibernate: 
    delete 
    from
        member_second_roles 
    where
        member_id=? 
        and second_roles=?

위 쿼리를 보다 시피 값타입은 삭제되지 않은 것도 insert 쿼리가 날라가고 EnumType 같은 경우 보다 정교히 삭제만 되는 것을 볼 수 있다.

DB 테이블에 들어가는 것

Member

Roles

Address

MemberSecondRoles

profile
기록을 통해 실력을 쌓아가자

0개의 댓글