빡코

[JPA][기본개념] 정리 본문

Java/JPA

[JPA][기본개념] 정리

chris.djang 2023. 2. 6. 13:02

 순서

    JPA 

    어노테이션 정리 

    @Entity

    @Id

    @GeneratedValue

     

    @Column

    @Column 어노테이션은 데이터베이스의 테이블에 있는 컬럼과 동일하게 1:1 매칭이 되기 떄문에 Entity 클래스안에 내부 변수로 정의됨. 만약 테이블에 a, b, c 컬럼이 있다면 각각 3개의 @Column 어노테이션을 작성하게 됨. 의도적으로 필요없는 컬럼들을 작성하지 않아도 됨. 데이터베이스 테이브에 실제 a, b, c, d 총 4개의 컬럼이 있더라도 a, b, c  컬럼만 Entity 클래스에 작성해도 무방하다는 의미. 

    @Column 어노테이션은 별다른 옵션을 설정하지 않는다면 생략이 가능함. 즉 Entity 클래스에 정의된 모든 내부변수 기본적으로 @Column  어노테이션이 있다고 볼 수 있음 

     

    @Embeddable: 내장 타입을 포함하였다 

    @Inheritance 

    @DiscriminatorColumn

     

    Entity

    직접로딩/지연로딩 

    cascade 영속성 전이 

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)

    연관관계 메서드 

     

     

    테스트 케이스를 위한 설정

    테스트 모드에서 실제 DB접그 없이 메모리 DB를 띄우고 테스하는 방법은 아래와 같다. 

    우선 Test Directory에 resource:application.yml 파일을 복사하여 붙여 넣어준다. 이러면 테스트 실행시 Test 하위의 yml 파일이 먼저 실행된다. 

    spring:
      datasource:
        url: jdbc:h2:mem:test #메모리 모드로 동작
        username: sa
        password:
        driver-class-name: org.h2.Driver
    
      jpa:
        hibernate:
          ddl-auto: create
        properties:
          hibernate:
           # show_sql:true
            format_sql: true
    logging.level:
        ort.hibernate.SQL: debug
        ort.hibernate.type: trace

    http://www.h2database.com/html/cheatSheet.html

     

     

    spring:
    #  datasource:
    #    url: jdbc:h2:mem:test #메모리 모드로 동작
    #    username: sa
    #    password:
    #   driver-class-name: org.h2.Driver
    
    # jpa:
    #  hibernate:
    #     ddl-auto: create
    #  properties:
    #     hibernate:
    #      # show_sql:true
    #       format_sql: true
    logging:
      level:
        ort.hibernate.SQL: debug
        ort.hibernate.type: trace

    스프링부트는 datasource 설정이없으면, 기본적을메모리 DB사용하고, driver-class현재등록된라이브러를보고찾아준다. 추가로ddl-autocreate-drop모드로동작한다. 따라서데이터소스나, JPA 관련된별도의추가설정을하지않아도된다.

     

     

    외부 클래스에서 객체 생성 막아주는 방법 

    1.생성자를 통해서 외부클래스에서 현 클래스 객체 생성 막기
    @Entity
    @Getter @Setter
    public class OrderItem {
        protected OrderItem(){ 
        }
    }
    
    2. @NoArgsConstructor 어노테이션 이용 외부클래스에서 현 클래스 객체 생성 막기
    @Entity
    @Getter @Setter
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class OrderItem {
       
    }

     

    도메인 모델 패턴

    엔티티(@Entity)가 비지니스 로직을 가지고 객체 지향의 특성을 적극활용하여 서비스(@Service) 계층에는 단순히 엔티티에 필요한 요청을 위임하는 역할만 맡기는 패턴(http://martinfowler.com/eaaCatalog/domainModel.html).

    반대로 서비스 계층에 대부분의 비지니스 로직을 처리하는 것을 트랜잭션 스크립트 패턴(http://martinfowler.com/eaaCatalog/transactionScript.html) 이라고 한다. 

    관점은 유지보수의 편리성에 중점을 두고 생각해보아야 한다. 

     

    Cascade의 범위:  Persist Life Cycle이 동일 범위내에 충족할 경우?

     

     

    통합 및 단위 테스트  

     

    JPA 쿼리 

     

    1.일반쿼리 

    public List<Order> findAllByString(OrderSearch orderSearch){
            return em.createQuery("select o from Order o join o.member m" +
                    " where o.status =: status" +
                    " and m.name like :name", Order.class)
                    .setParameter("status", Order.class)
                    .setParameter("name", orderSearch.getMemberName())
                    .setMaxResults(1000) //최대 천건
                    .getResultList();
    }

    2. JPQL로 처리 

    public List<Order> findAllByString(OrderSearch orderSearch){
          
            //동적쿼리
            //language=JPAQL
    
            String jpql ="select o From Order o join o.member m";
            boolean isFirstCondition =true;
    
            //주문상태검색
            if(orderSearch.getOrderStatus()!= null){
                if(isFirstCondition){
                    jpql +=" where";
                    isFirstCondition =false;
                }
                else {
                    jpql +=" and";
                }
    
                jpql +=" o.status = :status";
    
            }
    
            //회원이름검색
            if(StringUtils.hasText(orderSearch.getMemberName())){
                if(isFirstCondition){
                    jpql +=" where";
    
                    isFirstCondition =false;
                }
                else
                {
                    jpql +=" and";
                }
    
                jpql +=" m.name like :name";
            }
    
            TypedQuery<Order> query = em.createQuery(jpql, Order.class)
                    .setMaxResults(1000);
    
            if(orderSearch.getOrderStatus()!= null){
                query = query.setParameter("status", orderSearch.getOrderStatus());}
    
            if(StringUtils.hasText(orderSearch.getMemberName())){
                query = query.setParameter("name", orderSearch.getMemberName());
            }
    
            return query.getResultList();
    
        }

     

    3. JPA Criteria 쿼리 

        //JPA 표준 동적쿼리 제공
        public List<Order> findAllByCriteria(OrderSearch orderSearch) {
            CriteriaBuilder cb = em.getCriteriaBuilder();
            CriteriaQuery<Order> cq = cb.createQuery(Order.class);
            Root<Order> o = cq.from(Order.class);
            Join<Object, Object> m = o.join("member", JoinType.INNER);
            List<Predicate> criteria = new ArrayList<>();
    
            //주문 상태 검색
            if(orderSearch.getOrderStatus() != null) {
                Predicate status = cb.equal(o.get("status"), orderSearch.getOrderStatus());
                criteria.add(status);
            }
            
            //회원이름검색
            if(StringUtils.hasText(orderSearch.getMemberName())){
                Predicate name =
                        cb.like(m.<String>get("name"),"%"+orderSearch.getMemberName()+"%");
                criteria.add(name);
            }
            
            cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
            TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000);//최대1000건
            return query.getResultList();
    
        }

    변경 감지와 병합(merge)

    준영속 상태의 엔티티를 수정하는 방법 

    1. 변경 감지 사용(Dirty Checking) 

    2. 병합(merge) 사용 

     

    *준영속 엔티티란

    영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다. 임의로 만들어낸 Entity 라도 JPA가 기존 식별자(Id)를 가지고 있는 경우에도 준영속성 컨텍스트라고 할 수있다. 

     

    변경 감지 사용(Dirty Checking) 

       //변경 감지
        @Transactional
        public void updatedItem(Long itemId, Book param) {
            
            //파라미터로 넘어오 준영속상태의 데이터인 경우 변경된 값을 JPA가 직접 감지하여 update를 실행한다.
            Item findItem = itemRepository.findOne(itemId); //같은 Entity를 조회한다.
            //Item findItem = em.find(Item.class, itemParam.getId()); // 위와 같은 방법
            findItem.setPrice(param.getPrice()); // 데이터를 수정한다.
    
        }

    영속성콘텍스트에서 엔티티를 다시 조회한 후에 데이터를 수정하는 방법

    트랜잭션 안에서 엔티티를 다시 조회, 변경할 값 선택 > 트랜잭션 커밋 시점에 변경감지(Dirty Checking) 이동작 해서 데이터베이스에 UPDATE SQL 실행

     

    병합(merge) 사용 

    병합을 사용하면 모든 속성이 변경된다. 병합시 값이 없으면 null로 업데이트 할 위험이 있다. 병합된 모든 필드를 교체한다. 

     

    엔티티를 변경할 때는 항상 변경 감지를 사용해야한다.

    1. 컨트롤러에서 어설프게 엔티티 생성하지 마라.(setter를 생성하지 말라. 데이터 변경 지점을 Entity 한곳에 집중하여 추척을 쉽게한다)

    2. 트랜잭션이 있는 서비스 계층에 식별자(id)변경할 데이터를 명확하게 전달하세요.(파라미터 or dto)

    3. 트랜잭션이 있는 서비스 계층에서 영속상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경하세요.

    4. 트랜잭션 커밋시점에 변경감지가 실행됩니다.

    @Controller
    @RequiredArgsConstructor
    public class Item Controller {
      privatefinal ItemService itemService; /**     * 상품수정, 권장코드     */
      @PostMapping(value = "/items/{itemId}/edit") 
      public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
        itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());
        return "redirect:/items";
      }
    }
    
    
    package jpabook.jpashop.service;
    @Service 
    @RequiredArgsConstructor
    public class ItemService {
      privatefinal ItemRepository itemRepository; /**     * 영속성컨텍스트가자동변경     */
      @Transactional
      public void updateItem(Long id, String name, int price, int stockQuantity) {
        Item item = itemRepository.findOne(id);
        item.setName(name);
        item.setPrice(price);
        item.setStockQuantity(stockQuantity);
      }
    }

     

     

     

     

    **참고**

    https://www.icatpark.com/entry/JPA-%EA%B8%B0%EB%B3%B8-Annotation-%EC%A0%95%EB%A6%AC

    'Java > JPA' 카테고리의 다른 글

    [JPA][개념] 프록시와 연관관계 정리  (0) 2023.03.23
    [JPA][개념] 상속관계  (0) 2023.02.22
    [JPA][개념]다양한 연관관계 매핑  (0) 2023.02.17
    [JPA][개념] 엔티티 매핑  (0) 2023.02.14
    [JPA][개념] 영속성 관리  (0) 2023.02.10