한 Entity 가 여러 개의 Enum을 받고 싶을 때가 있다.
그럴때 유용하게 사용될 수 있는 방법이 있다.
일단 RoleType은
public enum RoleType {
USER,ADMIN
}
로 가정한다.
@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);
위 사진과 같이 잘 나타나는 것을 볼 수 있다.
@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 같은 경우 보다 정교히 삭제만 되는 것을 볼 수 있다.