본문 바로가기
카테고리 없음

TIL-21

by 오우지 2021. 10. 16.

값 타입의 비교

 

동일성 비교: 인스턴스의 참조 값을 비교, == 사용

동등성 비교: 인스턴스의 값을 비교, equals() 사용

값 타입은 a.equals(b)를 사용해서 동등성 비교를 해야 한다.

값 타입의 equals() 메소드를 적절하게 재정의

 

a.equals(b)의 기본은 ==연산이라 오버라이드 해줘야 한다.

 

값 타입 컬렉션

은 그냥 값 타입을 컬렉션에 담아서 쓰는 것을 이야기한다.

최근에는 JSON을 받으면서 컬렉션을 담을 방법이 생기긴 했지만 기본적으로 DB에는 컬렉션을 담을 방법이 없다. 따라서 컬렉션을 위한 별도의 테이블을 만들어야 한다.

 

값 타입을 하나 이상 저장할 때 사용

@ElementCollection, @CollectionTable 사용, 컬렉션을 위한 테이블

@Entity
public class Member extends BaseEntity{

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @Embedded
    private Address homeAddress;

    @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<>();

값 타입도 본인만의 라이프 사이클이 존재하지 않는다. Member에 의존한다. 그냥 username과 다를 바 없는 전략을 사용한다. 따라서 값 타입 컬렉션은 영속성 전이와 고아객체 제거 기능을 기본으로 가진다고 할 수 있다. 또한 컬렉션들은 지연로딩이다.

 

값 타입 컬렉션의 특성

1. 영속성 전이

2. 고아객체 제거

3. 지연로딩

 

            Member member = new Member();
            member.setUsername("member1");
            member.setHomeAddress(new Address("city1", "street", "12344"));

            member.getFavoriteFoods().add("치킨");
            member.getFavoriteFoods().add("족발");
            member.getFavoriteFoods().add("피자");

            member.getAddressHistory().add(new Address("city1", "street", "12344"));
            member.getAddressHistory().add(new Address("city1", "street", "12344"));

            em.persist(member);

            em.flush();
            em.clear();

            Member findMember = em.find(Member.class, member.getId());

            //HomeCity -> newCity
//            findMember.getHomeAddress().setCity("newCity");

            Address a = findMember.getHomeAddress();
            findMember.setHomeAddress(new Address("newCity", a.getStreet(), a.getZipcode()));
			
            //치킨 -> 한식
            findMember.getFavoriteFoods().remove("치킨");
            findMember.getFavoriteFoods().add("한식");

			//equals 재정의 필수!
            findMember.getAddressHistory().remove(new Address("old1", "street", "10000"));
            findMember.getAddressHistory().add(new Address("newCity1", "street", "10000"));
			//여기서 쿼리는 하나만 지우고 하나를 추가하는 로직이 아니라
            //전부 드롭하고 재삽입해준다.
            
            
            tx.commit();

값 타입 컬렉션의 제약사항

- 값 타입은 엔티티와 다르게 식별자 개념이 없다.

- 값은 변경하면 추적이 어렵다.

- 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 관련된 모든 데이터를 삭제하고 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.

- 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 함: null 입력 X, 중복 저장 X

 

다음과 같은 제약사항 때문에 컬렉션이 아니라 AddressEntity같은 엔티티로 만들어서 List로 추가해 주는 것이 좋다.

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressHistory = new ArrayList<>();

JPA에서는 프록시도 고려해야 하기 때문에 equals를 구현할 때 직접 접근이 아닌 getter를 이용한 접근을 해줘야 한다.

 


객체지향 쿼리 언어1 - 기본 문법

 

JPA는 다양한 쿼리 방법을 지원하는데

JPQL, JPA Criteria QueryDSL, 네이티브SQL(connectBy등 DB종속적 쿼리를 써야 할 때), JDBC API를 직접 사용하는 방법 등이 있다.

 

1. JPQL : 가장 단순한 조회 방법

-EntityManager.find()

-객체 그래프 탐색(a.getB().getC())

 

JPA을 사용하면 엔티티 객체를 중심으로 개발해야 되기 때문에 검색 쿼리 시에 문제가 생긴다. 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색해야 하기 때문에 모든 DB 데이터를 객체로 변환해서 검색하는 것이 불가능 하다. 따라서 애플리케이션이 필요한 데이터만 DB에서 불러오려면 검색 조건이 포함된 SQL이 필요하다.

 

따라서, JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공한다. SQL 문법과 유사하고 SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원

 

JPQL은 엔티티 객체를 대상으로 쿼리

SQL은 데이터베이스 테이블을 대상으로 쿼리

 

2. Criteria

-문자가 아닌 자바코드로 JPQL을 작성할 수 있음

-JPQL 빌더 역할

-JPA 공식 기능

-너무 복잡하고 실용성이 없다.

 

집어치우고 QueryDSL쓰자

3. QueryDSL

-문자가 아닌 자바코드로 JPQL을 작성할 수 있음

-JPQL 빌더 역할

-컴파일 시점에 문법 오류를 찾을 수 있음

-동적쿼리 작성 편리함

-단순하고 쉬움

-실무 사용 권장

JPQL만 잘하면 QueryDSL은 덤이니까 JPQL부터 잘해야된다.

 

JPQL 문법

 

select m from Member as m where m.age > 18

-엔티티와 속성은 대소문자 구분 O(Member, age)

-JPQL 키워드는 대소문자 구분 X(select, from, where)

-엔티티 이름 사용, 테이블 이름 X

-별칭은 필수(m) (as는 생략 가능)

 

집합과 정렬 지원

select

    COUNT(m),

    SUM(m.age),

    AVG(m.age),

    MAX(m.age),

    MIN(m.age),

from Member m

-GROUP BY, HAVING, ORDER BY 가능

 

TypeQuery:반환 타입이 명확할 때 사용

Query: 반환 타입이 명확하지 않을 때 사용

 

query.getResultList(): 결과가 하나 이상일 때, 리스트 반환

-결과가 없으면 빈 리스트 반환

query.getSingleResult(): 결과가 정확히 하나, 단일 객체 반환

결과가 없으면 javax.NoResultException

둘 이상이면 javax.NonUniqueResultException

 

Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);
em.createQuery("select m from Member m where m.username = :username", Member.class)
	.setParameter("username", "member1")
	.getSingleResult();
System.out.println("singleResult = " + singleResult.getUsername());