2023-06-22-TIL

Todady I Studied

API Rate Limit vs Throttling

요청이 너무 많은 경우 429를 응답하면서 트래픽 유입량을 조절할 수 있다.

  • https://12bme.tistory.com/504
  • https://www.redhat.com/architect/pros-and-cons-throttling
  • https://www.enjoyalgorithms.com/blog/throttling-and-rate-limiting
  • https://nordicapis.com/api-rate-limiting-vs-api-throttling-how-are-they-different/
  • https://blog.stoplight.io/best-practices-api-rate-limiting-vs-throttling
  • https://learn.microsoft.com/en-us/azure/architecture/patterns/throttling

Namas:

  • IP로 차단하는것은 의미가 없다. VPN으로 우회해서 얼마든지 어뷰징이 가능
  • CloudFlare에서는 특정 패턴의 요청을 막는 등 정책 설정이 가능했다.

SSD vs HDD

Today I Learned

@DynamicUpdate Is Bad For Performance?

  • https://hyune-c.tistory.com/entry/DynamicUpdate-%ED%99%9C%EC%9A%A9%EA%B8%B0
  • https://docs.jboss.org/hibernate/orm/6.1/javadocs/org/hibernate/annotations/DynamicUpdate.html
  • https://www.baeldung.com/spring-data-jpa-dynamicupdate
  • https://dzone.com/articles/when-to-use-the-dynamicupdate-with-spring-data-jpa
  • https://stackoverflow.com/questions/3404630/hibernate-dynamic-update-dynamic-insert-performance-effects
  • https://vladmihalcea.com/?s=dynamic+update&submit=Go
  • https://vladmihalcea.com/how-to-update-only-a-subset-of-entity-attributes-using-jpa-and-hibernate/
  • https://vladmihalcea.com/jpa-criteria-api-bulk-update-delete/
  • https://vladmihalcea.com/bulk-update-optimistic-locking/
  • https://vladmihalcea.com/bulk-update-delete-blaze-persistence/
  • https://vladmihalcea.com/hibernate-dynamic-update-json-properties/
  • https://vladmihalcea.com/how-to-update-only-a-subset-of-entity-attributes-using-jpa-and-hibernate/
  • https://thorben-janssen.com/dynamic-inserts-and-updates-with-spring-data-jpa/
  • http://www.java2s.com/Questions_And_Answers/JPA/Performance/dynamic.htm

Spring Transaction

  • https://narusas.github.io/2019/07/17/Spring-Transaction-Note.html
  • https://www.youtube.com/watch?v=_WkMhytqoCc

Custom Primary Key Generator in JPA

직접 생성해내는 문자열, 일련번호 등으로만 구성된 키라면 다양한 시점에 다양한 방법으로 생성할 수 있을것이다. 영속계층에 들어가기 직전(em.persist() 전)에 IdentifierGenerator를 통해서, save 메서드를 호출하기전 setter를 통해서, Persistable을 구현하여 ID를 설정하는 등 방법은 많다.

하지만 나의 경우는 DB에서 다른 테이블의 키값을 쿼리를 통해 조회한 다음, 새로운 생성값을 조합해서 키를 생성하는 방식이다. 따라서 DB 쿼리가 무조건 한 번은 일어나는데, 엔티티 클래스에서 이를 포함하는건 매우 부자연스럽고 권고되지 않는다. 그래서 IdentifierGenerator를 상속받아서 generate메서드를 override하여 파라미터로부터 엔티티를 받아온 다음, JPQL을 통해 쿼리를 실행하여 키를 생성했다.

@Override
public Object generate(SharedSessionContractImplementor session, Object object) {
if (object instanceof Entity entity) {
    String albumId = track.getAlbum().getAlbumId();
    String lastId =
        session
            .createQuery(
                "select max(e.id) from Entity e where e.otherEntity.id = :id",
                String.class)
            .setParameter("id", id)
            .getSingleResultOrNull();
    return // TODO: key generate logic ...
}
return null;
}

이처럼 SharedSessionContractImplementor를 이용해서 JPQL 쿼리를 실행하는데, Object로 넘어오는 파라미터는 엔티티이므로, instanceof로 감싸서 연관관계가 매핑된 다른 엔티티의 id값을 가져오는 쿼리를 작성할 수 있었다.

  • https://stackoverflow.com/questions/37160332/custom-primary-key-generator-in-jpa
  • https://medium.com/@pushpakmittal/custom-sequence-generator-spring-data-jpa-d82452eb1ad8
  • https://thorben-janssen.com/jpa-generate-primary-keys/
  • https://jpa-buddy.com/blog/the-ultimate-guide-on-client/

Entity Lifecycle Model in JPA and Hibernate

  • https://thorben-janssen.com/entity-lifecycle-model/

GROUP_CONCAT in JPA and QueryDSL

  • https://cheese10yun.github.io/jpa-query-dsl-group-concat/
  • https://www.popit.kr/jpa-querydsl-group_concat-%EC%82%AC%EC%9A%A9%EB%B2%95/
  • https://ksk-developer.tistory.com/49
  • https://lightrun.com/answers/querydsl-querydsl-group_concat-does-not-work-in-querydsl
  • https://dico.me/java/articles/290/ko
  • https://prohannah.tistory.com/118

MySQLDialect를 직접 상속받는 클래스를 등록하고 "GROUP_CONCAT"을 등록하는 방식으로 많이 설명이 되어있는데, 나의 경우에는 Spring Boot 3.1.1 버전에 대한 JPA, 그리고 QueryDSL 5.0.0을 사용해서 그런지 동작하지 않았다. 생각보다 단순하게 stringTemplate을 사용하여 "GROUP_CONCAT"이라는 문자열을 우겨넣어서 해결했다.

  private static JPQLQuery<String> selectArtists() {
    return JPAExpressions.select(Expressions.stringTemplate("group_concat({0})", artist.artistName))
        .from(artist)
        .where(
            artist.artistId.in(
                JPAExpressions.select(trackArtist.artist.artistId)
                    .from(trackArtist)
                    .where(trackArtist.track.trackId.eq(track.trackId))));
  }

"VALUE".in({SubQuery…}) in QueryDSL

원래는 "VALUE".in({SubQuery…}) 와 같은 형식으로 IN절을 통해서 내가 원하는 값이 서브쿼리 결과에 있는지를 조건으로 걸고 싶었지만, QueryDSL에서 상수값을 IN절의 앞에 오게하는 방법은 없는것 같다. 결국 같은 결과를 가져오기 위해서 count 조건절에 검색값을 넣고 count쿼리의 결과가 0보다 큰지를 검사했다. feat. ChatGPT

JPAExpressions.select(videoArtist.count())
          .from(videoArtist)
          .where(
              videoArtist
                  .video
                  .videoId
                  .eq(video.videoId)
                  .and(videoArtist.artist.artistName.contains(searchValue)))
          .gt(0L);

EXIST in QueryDSL

  • https://suyou.tistory.com/286
  • https://jojoldu.tistory.com/516
  • https://velog.io/@shwncho/QueryDsl-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0exists-%EB%8B%A4%EC%A4%91-%EC%A1%B0%EA%B1%B4

SubQuery in QueryDSL

  • https://jojoldu.tistory.com/379

Distinct vs Group By

  • https://medium.com/@smitagudale712/distinct-vs-group-by-in-sql-server-b62527285f9c

Books

  • https://product.kyobobook.co.kr/detail/S000202720436

References

  • https://docs.jboss.org/hibernate/orm/6.2/userguide/html_single/Hibernate_User_Guide.html
  • https://hibernate.org/orm/documentation/6.2/