JPA 학습 정리

김진회·2022년 11월 22일
0

jpa

목록 보기
1/5

0. JPA (Java Persisence API)

  • 자바의 ORM(Object Relational Mapping) 표준 스펙을 정의
  • JPA의 스펙은 자바의 객체와 DB를 어떻게 매핑하고 동작해야 하는지를 정의하고 있음

1. JPA 사용이유

  • SQL 중심적 개발 -> 객체 중심적 개발
  • 생산성, 유지보수, 성능, 추상화, 독립성 등 향상
  • 패리다임의 불일치 해결
  • 유지보수 용이
저장: jpa.persist(member)
조회: jpa.find(memberId)
수정: member.setName("변경할 이름")
삭제: jpa.remove(member)
사용자쿼리: TypeQuery<Product> query = jpa.createQuery("select p from Product as p", Product.class) //타입 명확할 때는 타입지정한 변수로 받거나 TypeQuery
            Query query = jpa.createQuery("select p.id, p.name from Product as p") //타입 지정 불가일 때 Query
            Product result = query.getSingleResult() //결과가 하나일 때
            List<Product> result = query.getResultList() //결과가 하나 이상일 때 (결과없으면 빈 리스트)

2. 어노테이션(loombok 포함)

@Entity: 엔티티 지정
@Table: Table명 지정
@Builder: 빌더 자동 생성
@Getter: getter 자동 생성
@Setter: setter 자동 생성

기본키

@Id: 기본키 지정
@GeneratedValue(strategy = GenerationType.xx): 값 자동 생성

  • AUTO: DB 벤더에 따라 자동으로 3가지 전략 중 하나를 선택
  • IDENTITY: 기본키 생성을 DB에게 위임 (MySQL의 경우는 AUTO INCREMENT)
  • TABLE: 벤더에 의존적이지 않음. 시퀀스 테이블을 만들어 db시퀀스를 흉내낼 Id를 할당
  • SEQUENCE: DB의 sequence 객체를 이용해 유일한 값을 순서대로 생성
    이 전략은 sequence를 사용하는 Oracle, DB2, H2 등의 DB에서 사용

Column

@Column(): 컬럼에다 지정

  • name="" : 이름 지정
  • nullable=true : not null
  • unique = true : 유일한 값으로 설정. 이 방법보다 클래스 위에 어노테이션으로 제약을 거는 방법을 추천
    • updatable = false : 수정 불가
    • columnDefinition="" : 컬럼type이나 default를 붙여 초기값 지정

@CreatedDate: type이 시간일 때 사용 생성 시간값 할당

외래키

@JoinColumn : 다른 entity와 외래키 지정. @Column처럼 ()안에 설정 가능
@ManyToOne: 다대일 (본 엔티티가 다) ex.Product
@OneToMany: 일대다 (본 엔티티가 일) ex.Category
@OneToOne: 일대일
@ManyToMany: 다대다. 실무에서 쓰는 것 비추

@ManyToOne(targetEntity = Category.class, fetch = FetchType.LAZY) // LAZY는 영속성관련. 지연로딩
@JoinColumn(name="type", nullable=false)
private Category category // Category 엔티티의 type과 외래키로 사용
  • fetch: 영속성 관련. 지연로딩, 즉시로딩
    - LAZY: 지연로딩. Proxy 객체 조회. 조인값을 사용하는 시점에 조인값 조회
    - EAGER: 즉시로딩. 실제 객체 조회. 쿼리를 한 번에 실행. 실무에서 즉시 로딩은 거의 쓰지 않음
    @ManyToOne(targetEntity = Category.class, fetch = FetchType.LAZY) //LAZY: JPA 영속성,값을 꺼내쓸 때 얘를 조인해줘
    @JoinColumn(name = "type", nullable = false)
    private Category category;
    TIP: @ManyToOne, @OneToOne은 기본이 즉시 로딩이니 LAZY 설정을 필수적으로 하자

CASCADE

  • @OneToMany, @OneToOne에만 사용가능. 여기에 cascade 설정하기
  • cascade = CascadeType.ALL
    • ALL: 모두 적용
    • PERSIST: 영속(상위 엔티티 저장 시, 하위 엔티티도 저장)
    • REMOVE: 삭제(상위 엔티티 저장 시, 하위 엔티티도 삭제)
    • MERGE, REFRESH, DETACH
  • orphanRemoval=true: 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제 (고아 객체 제거)
@OneToMany(mappedBy="category", cascade=CascadeType.PERSIST, orphanRemoval = true)
private List<Product> products;

3. JPQL

  • JPA는 다양한 쿼리 방법을 지원 (JPQL, QuertDSL, JPA Criteria, 네이티브 SQL, JDBC API etc...)
  • JPQL(Java Persistence Query Language) : 가장 단순한 조회 방법. 객체 그래프 탐색. 엔티티 객체를 대상으로 쿼리를 질의
  • createQuery: 사용자 지정 쿼리 생성. 별칭을 꼭 넣자
  • TypeQuery: 타입 명확할 때는 타입지정한 변수로 받거나 TypeQuery
  • Query: /타입 지정 불가일 때 Query
  • getSingleResult(): 결과가 하나일 때
  • getResultList(): 결과가 하나 이상일 때 (결과없으면 빈 리스트)
String query = "select m from Member m where m = :member";
List<Team> result = em.createQuery(query, Member.class)
            .setParameter("member", member1)
            .getResultList();

4. JPA 페이징

  • JPA는 페이징을 다음 두 API로 추상화 해준다
  • setFirstResult(int startPosition): 조회 시작위치(0부터 시작)
  • setMaxResults(int maxResult): 조회할 데이터 수
String query = "select m from Member m order by m.name desc";
List<Member> result = em.createQuery(query, Member.class)
            .setFirstResult(10)
            .setMaxResult(20)
            .getResultList();

5. Repository 기본 메소드

  • jparepository를 extends한 인터페이스는 아래와 같은 메소드를 기본으로 제공한다.
    • findAll(): 테이블에서 전체 목록을 조회
    • findById(id): 기본키가 id인 데이터 조회
    • save(object): object를 테이블에 저장
    • saveAll(objectList): objectList를 테이블에 저장
    • delete(object): object를 테이블에 삭제
    • deleteAll(objectAll): objectList를 테이블에 삭제
    • count(): 전체 데이터 수 리턴
    • exists(id): 기본키가 id인 데이터가 있는지 리턴
    • flush(): 지금까지 테이블에 대한 데이터 변경 작업들이 디스크에 모두 기록
  • 그 외 사용자 지정 메소드는 규칙에 맞춰 선언해야함
    • findByName(name), countByName(name): 컬럼명에 맞춰 Name을 입력
    • 포함 가능 키워드: And, Or, Between, LessThan, GreaterThanEqual, Like, IsNull, In, OrderBy
      • ex) findByEmailAndUserId(String email, String userId)
    • Top, Count 등도 사용 가능
      • ex) findTop3ByNameOrderByCreateAtDesc(name);
public interface AccountRepository extends JpaRepository<Account, Long> {
    Optional<Account> findByEmail(String email);
    Optional<Account> findByNickname(String nickname);
}

6. MapStructure (맵퍼)

https://velog.io/@kku64r/mapstruct


7. JpaSpecification (필터링)

필터링을 할 때 쓰기에 좋다고 생각했다.
하지만 JPA/김영한님의 강의에서 매우 비추천을 하셨고, 앞으로 사용 안 할 예정이다.
그러니 쿼리DSL을 쓰자!

  • CasesSpecification.java

    package com.service.domain.product.service.specification;
    
    import com.service.domain.product.entity.Cases;
    import org.springframework.data.jpa.domain.Specification;
    
    import javax.persistence.criteria.CriteriaBuilder;
    import javax.persistence.criteria.Predicate;
    import javax.persistence.criteria.Root;
    import java.util.List;
    
    public class CasesSpecification {
        public static Specification<Cases> containsName(String name) {
            return (root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("name"), "%"+name+"%");
        } // 이름 포함 검색
        public static Specification<Cases> equalCorp(List<String> corp) {
            return (root, query, criteriaBuilder) -> root.get("corp").in(corp);
        } // 회사 동일 검색
        public static Predicate equalExtendedAtx(CriteriaBuilder cb, Root root, boolean extendedAtx) {
            return cb.equal(root.get("extendedAtx"), extendedAtx);
        } // extendedAtx 동일 검색(predicate. spec으로 변환 필요)
        public static Specification<Cases> betweenPrice(int minPrice, int maxPrice) {
            return (root, query, criteriaBuilder) -> criteriaBuilder.between(root.get("price"), minPrice, maxPrice);
        } //가격 사이 검색
        public static Specification<Cases> result(Predicate p) {
            return (root, query, criteriaBuilder) -> p;
        } // Predicate -> Specification
    }
    
  • ProductServiceImpl.java의 getCaseList() 메소드

    @Override
        public ProductListDto getCasesList(CasesRequest request, int page, int desc) {
            CriteriaBuilder cb = entityManager.getCriteriaBuilder();
            CriteriaQuery<Cases> criteriaQuery = cb.createQuery(Cases.class);
            Root<Cases> itemRoot = (Root<Cases>) criteriaQuery.from(Cases.class).alias("generatedAlias0"); //별칭해줘야함
            Predicate p = null;
    
            Specification<Cases> spec = (root, query, criteriaBuilder) -> null;
            if (request.getName() != null) {
                spec = spec.and(CasesSpecification.containsName(request.getName()));
            }
            if (request.getCorp() != null && request.getCorp().size() > 0) {
                spec = spec.and(CasesSpecification.equalCorp(request.getCorp()));
            }
            if (request.isExtendedAtx()) { //or이라서 Predicate로 만들고 후에 spec에 포함
                if (p == null)
                    p = CasesSpecification.equalExtendedAtx(cb, itemRoot, true);
                else
                    p = cb.or(p, CasesSpecification.equalExtendedAtx(cb, itemRoot, true));
            }
            if (request.getPrice() != null && request.getPrice().size() > 1) {
                spec = spec.and(CasesSpecification.betweenPrice(request.getPrice().get(0), request.getPrice().get(1)));
            }
            if (p != null)
                spec = spec.and(CasesSpecification.result(p)); //Predicate를 Spec에 포함
    
            //페이지네이션
            Pageable pageable = getPageable(page, desc);
            Page<Cases> pageCasess = casesRepository.findAll(spec, pageable);
            List<Cases> casess = pageCasess.getContent();
            int totalPages = pageCasess.getTotalPages();
    
            ~~~
            ~~~
    
            return ProductListDto.builder()
                    .productDtoList(list)
                    .totalPage(totalPages)
                    .build();
        }

    ...지금봐도 복잡하다. And와 Or 같이 있을 때 특히 힘들다. 쓰지말자. 쿼리DSL쓰자.
    spec 참조글 추천 https://jforj.tistory.com/95


8. QueryDSL


9. Transactional

추후 정리


10. N+1문제

https://velog.io/@kku64r/nplus1

profile
SSAFY 7기. HMG. 협업, 소통, 사용자중심

0개의 댓글