Chris940915 / geo_benchmark

0 stars 0 forks source link

1/10 #14

Open Chris940915 opened 3 years ago

Chris940915 commented 3 years ago

@Valid, Errors

https://ande226.tistory.com/102 객체 값 검증하기.

Chris940915 commented 3 years ago

GSON

Python 으로 구현되어있는 경우, json 을 자유자재로 다룰 수 있다. 프로젝트에서 데이터를 주고받을 때, json 형식을 많이 사용해서 구글의 JSON 라이브러리 GSON의 사용법이 제일 간단해 보여서 사용.

dependencies { implementation 'com.google.code.gson:gson:2.8.6' } Gradle에 dependencies에 추가.

Gson gsonObj = new Gson();
Map<String, String> inputMap = new HashMap<String, String>();
String jsonStr = gsonObj.toJson(inputMap);

JsonObject

JsonObject는 객체(대부분 String)을 Json객체로 바꿔주거나 Json객체를 새로 만드는 역할.

JsonObject jsonVar = new JsonObject();
jsonVar.put("key1", "value1"); // json 객체에 key가 "key1"이고, value가 "value1" 인 요소 추가 
jsonVar.get("key1"); // key 값이 "key1"인 요소 추출. 

JsonArray

Json 들이 들어있는 Array

JsonObject json1 = new JosnObject();
json1.put("첫번째" : 1);
JsonObject json2 = new JsonObject();
json2.put("두번째" : 2);

JsonArray jsonArray = new JsonArray();
jsonArray.put(json1);
jsonArray.put(json2);

// [ {"첫번째 : 1} , { "두번째" : 2}]

JsonArray 배열에 한개씩 접근해서 List 변환 방법.

JsonArray jsonArray = new JsonArray();
List<String> stringArray = new ArrayList<>();
for(int i = 0; i < jsonArray.size(); i++){
    JsonElement jsonElement = jsonArray.get(i);
    stringArray.add(jsonElement.getAsJsonObject());
}

특정 path Json load 방법.

JsonParser paser = new JsonPrser();
JsonArray jsonArray = (JsonArray) parser.parse(new FileReader(path)); 

Optional

join fetch.

N+1 문제.

Chris940915 commented 3 years ago

N+1 문제.

image 이렇게, Member 와 Orders 가 One To Many, Orders와 Order_Item 이 Many To one 관계에 있을때. OrderRepository.findAll();을 호출하면 실제 수행되는 쿼리는 SELECT * FROM Orders와 같다. 하지만 여기서 멈추지않고 Orders와 관계가 있는 Member와 OrderItem에 대해서도 쿼리가 수행된다.

SELECT * FROM Member WHERE member.id = 1; 
SELECT * FROM Member WHERE member.id = 2; 
SELECT * FROM Member WHERE member.id = 3;
-- ...
-- (N개)

기존 쿼리의 결과로 나오는 N개와 관계 있는 테이블에 이 N개의 쿼리가 또 수행되는 것이 바로 N+1 쿼리 문제.

join fecth

JPQL에서 성능 최적화를 위해 제공하는 기능. 연관된 엔티티나 컬렉션을 한번에 같이 조회하는 기능으로 join fetch 명령어로 사용 가능.

em.createquery("select o from order o join fetch o.member");

위와 같이 join fetch를 사용하면 연관된 엔티티나 컬렉션을 함께 조회하며 위에선 parent와 One To Many 관계인 child 를 함께 조회해 다음과 같이 결과.

Order{id=1, name = 'Order1', member=Member{id=1, name = 'member1'}},
Order{id=2, name = 'Order2', member=Member{id=1, name = 'member1'}},
Order{id=3, name = 'Order3, member=Member{id=1, name = 'member1'}},
Order{id=14 name = 'Order14', member=Member{id=1, name = 'member1'}},
...

일대 다 관계인 컬렉션도 join fetch를 할 수 있다.

Select o from Order o join fetch o.order_item oi where o.id = 1;
Order{id=1, name = 'order1',
    orderitem = [
           OrderItem(id = 1,  ... ),
           OrderItem(id = 2,  ... ),
           OrderItem(id = 3,  ... ),
           ...
      ]
}

이 일대 다(컬렉션) 페치 조인을 할 때 유의할 점은 중복되는 OrderItem 데이터를 가져올 수 있다. 이때 사용해야하는 것이 Distinct이다. JPQL의 Distinct는 기존 SQL의 기능으로 중복된 결과를 제거하고 여기에 더하여 영속성 컨텍스트에서 한번 더 중복을 제거 ( SQL 기능으로 중복된 결과를 제거할때는 연관 관계에 있는 field의 데이터까지 동일해야하기 때문에 모든 중복이 다 사라지지 않아서 한번 더 제거.)

문제점 및 해결방안.

Chris940915 commented 3 years ago

fetch join

    public List<Order> findAllwithOrder() {
        return em.createQuery("select o from Order o " +
                                " join fetch o.member m " , Order.class)
                .getResultList();
    }
select
        order0_.order_id as order_id1_6_0_,
        member1_.member_id as member_i1_4_1_,
        order0_.delivery_id as delivery4_6_0_,
        order0_.member_id as member_i5_6_0_,
        order0_.order_date as order_da2_6_0_,
        order0_.status as status3_6_0_,
        member1_.city as city2_4_1_,
        member1_.street as street3_4_1_,
        member1_.zipcode as zipcode4_4_1_,
        member1_.name as name5_4_1_ 
    from
        orders order0_ 
    inner join
        member member1_ 
            on order0_.member_id=member1_.member_id

inner join

    select
        order0_.order_id as order_id1_6_,
        order0_.delivery_id as delivery4_6_,
        order0_.member_id as member_i5_6_,
        order0_.order_date as order_da2_6_,
        order0_.status as status3_6_ 
    from
        orders order0_ 
    inner join
        member member1_ 
            on order0_.member_id=member1_.member_id
    public List<Order> findAllwithOrder() {
        return em.createQuery("select o from Order o " +
                                " inner join o.member m " , Order.class)
                .getResultList();
    }
Chris940915 commented 3 years ago

Member 와 Order 사이가 다대일 @MnayToOne 관계로 매핑 되어있을때, fetch 타입을 줄 수 있다.

@Entity
@Getter @Setter
public class Order {
    @Id @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member Member;
    private LocalDateTime orderdate; 
}

만약 Order 에서 find를 수행한다면 원래는 Member 엔티티도 가져와야한다. 하지만, 지연로딩(LAZY)설정을 통해 실제 Member 객체를 사용하는 시점에 DB로 쿼리가 나간다. 따라서 지연로딩 설정을 하면 SELECT 쿼리가 따로따로 2번 나가게 된다. (네트워크를 2번 타게 된다는 뜻.)

그래서 즉시 로딩(EAGER)을 사용하는게 맞다고 생각할 수 있다. 조회할 때 한방에 다 조회하기 때문에 하지만, 즉시 로딩을 사용하면 예상치 못한 SQL이 발생할 수 있다. 예를 들어, 5개의 @ManyToOne이 모두 즉시 로딩으로 설정이 되어있을때 2개의 @ManyToOne만 조회하고 싶지만 5개 모두 조회하게 된다. 따라서 기본적으로 FetchType을 지연 로딩으로 설정하자.