programing

JUQ를 사용한 Spring Boot과 Spring Data JPA의 기술적 차이

telebox 2023. 7. 6. 22:08
반응형

JUQ를 사용한 Spring Boot과 Spring Data JPA의 기술적 차이

JUQ와 함께 Spring Data JPA over Spring Boot를 사용하거나 그 반대의 경우는 언제입니까?

스프링 데이터 JPA가 기본 CRUD 쿼리를 완료하는 데 사용될 수 있다는 것은 알고 있지만, JUQ를 사용하는 동안 복잡한 조인 쿼리에는 사용할 수 없다는 것이 더 쉬운 일입니까?

EDIT: jooq와 함께 Spring data jpa를 모두 사용할 수 있습니까?

당신의 질문에 대한 쉬운 답은 없습니다.저는 그 주제에 대해 몇 번 이야기를 했습니다.때때로 프로젝트에 둘 다 필요한 좋은 이유가 있습니다.

편집: 방언 및 데이터 유형과 관련하여 데이터베이스에 대한 IMHO 추상화는 여기서 주요 포인트가 아닙니다!!jOOQ는 지정된 대상 방언에 대한 SQL을 생성하는 데 매우 효과적이며 JPA/Hibernate도 마찬가지입니다.저는 jOOQ가 Postgres나 Oracle처럼 모든 기능이 없는 데이터베이스를 위한 기능을 에뮬레이트하기 위해 추가적인 노력을 기울였다고 말할 수 있습니다.여기서 질문은 "SQL이 제공하는 모든 에 대해 스스로 질문을 표현할 수 있기를 원합니까, 아니면 JPA가 표현할 수 있는 것에 만족합니까?"입니다.

다음은 두 가지를 함께 실행하는 예입니다.Spring Data JPA가 제공하는 저장소에 사용자 지정 확장 기능이 있습니다(인터페이스 및 구현 필요).와 JPA를 에 주입하도록 .EntityManagerjOOQ 컨텍스트뿐만 아니라.그런 다음 jOOQ를 사용하여 쿼리를 생성하고 JPA를 통해 실행합니다. 그 이유는 무엇입니까?JPA로는 문제의 질의 표현이 불가능하기 때문에 ("내가 가장 많이 들었던 것을 주세요.") 카운트 수가 가장 많은 것은 아니지만 여러 개일 수 있습니다.

제가 JPA를 통해 쿼리를 실행하는 이유는 간단합니다. 다운스트림 사용 사례에서는 JPA 엔티티를 전달해야 할 수 있습니다. jOOQ는 물론 이 쿼리 자체를 실행할 수 있으며 원하는 대로 레코드 작업이나 매핑을 수행할 수 있습니다.하지만 두 가지 기술을 모두 사용할 수 있는지에 대해 특별히 질문하셨듯이, 이것이 좋은 예라고 생각했습니다.

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.SelectQuery;
import org.jooq.conf.ParamType;
import org.jooq.impl.DSL;
import org.springframework.data.repository.CrudRepository;

import static ac.simons.bootiful_databases.db.tables.Genres.GENRES;
import static ac.simons.bootiful_databases.db.tables.Plays.PLAYS;
import static ac.simons.bootiful_databases.db.tables.Tracks.TRACKS;
import static org.jooq.impl.DSL.count;
import static org.jooq.impl.DSL.rank;
import static org.jooq.impl.DSL.select;

public interface GenreRepository extends 
        CrudRepository<GenreEntity, Integer>, GenreRepositoryExt {

    List<GenreEntity> findAllByOrderByName();
}

interface GenreRepositoryExt {
    List<GenreWithPlaycount> findAllWithPlaycount();

    List<GenreEntity> findWithHighestPlaycount();
}

class GenreRepositoryImpl implements GenreRepositoryExt {

    private final EntityManager entityManager;

    private final DSLContext create;

    public GenreRepositoryImpl(EntityManager entityManager, DSLContext create) {
        this.entityManager = entityManager;
        this.create = create;
    }

    @Override
    public List<GenreWithPlaycount> findAllWithPlaycount() {
        final Field<Integer> cnt = count().as("cnt");
        return this.create
                .select(GENRES.GENRE, cnt)
                .from(PLAYS)
                .join(TRACKS).onKey()
                .join(GENRES).onKey()
                .groupBy(GENRES.GENRE)
                .orderBy(cnt)
                .fetchInto(GenreWithPlaycount.class);
    }

    @Override
    public List<GenreEntity> findWithHighestPlaycount() {
        /*
        select id, genre 
        from (
          select g.id, g.genre, rank() over (order by count(*) desc) rnk 
            from plays p
            join tracks t on p.track_id = t.id
            join genres g on t.genre_id = g.id
           group by g.id, g.genre
        ) src
        where src.rnk = 1;
        */
        final SelectQuery<Record> sqlGenerator = 
        this.create.select()
                .from(
                        select(
                                GENRES.ID, GENRES.GENRE, 
                                rank().over().orderBy(count().desc()).as("rnk")
                        ).from(PLAYS)
                        .join(TRACKS).onKey()
                        .join(GENRES).onKey()
                        .groupBy(GENRES.ID, GENRES.GENRE)
                ).where(DSL.field("rnk").eq(1)).getQuery();

         // Retrieve sql with named parameter
        final String sql = sqlGenerator.getSQL(ParamType.NAMED);
        // and create actual hibernate query
        final Query query = this.entityManager.createNativeQuery(sql, GenreEntity.class);
        // fill in parameter
        sqlGenerator.getParams().forEach((n, v) -> query.setParameter(n, v.getValue()));
        // execute query
        return query.getResultList();
    }
}

저는 이것에 대해 몇 번 얘기했습니다.그 기술에는 아무런 도움이 되지 않습니다. 때때로 그것은 매우 희박한 판단입니다.

자세한 내용은 다음과 같습니다. https://speakerdeck.com/michaelsimons/live-with-your-sql-fetish-and-choose-the-right-tool-for-the-job

녹음된 버전과 함께 https://www.youtube.com/watch?v=NJ9ZJstVL9E .

전체 작동 예는 여기 https://github.com/michael-simons/bootiful-databases 입니다. https://github.com/michael-simons/bootiful-databases .

IMHO는 데이터베이스를 핵심으로 사용하는 성능과 유지 관리가 가능한 응용프로그램을 원하는 경우 데이터베이스를 사용한다는 사실을 추상화하고 싶지 않습니다.JUQ는 실제 쿼리를 코드에서 읽고 쓸 수 있지만 유형 안전을 사용할 수 있기 때문에 완전한 제어를 제공합니다.

JPA는 OO 모델을 수용하며, 이는 모든 경우에 데이터베이스가 작동하는 방식과 일치하지 않으며, 필드에 잘못된 주석을 달았기 때문에 N+1과 같은 예기치 않은 쿼리가 발생할 수 있습니다.주의를 기울이지 않으면 응용 프로그램을 확장할 때 성능 문제가 발생합니다. JPA 기준은 약간 도움이 되지만 쓰고 읽는 것은 여전히 훨씬 어렵습니다.

따라서 JPA를 사용하면 먼저 SQL에 쿼리를 작성한 다음 반나절을 사용하여 기준으로 변환할 수 있습니다.수년간 두 프레임워크를 모두 사용한 후 저는 간단한 CRUD 애플리케이션에도 JUQ를 사용할 것입니다(단순한 CRUD 애플리케이션은 없기 때문에 :-)

편집: JPA와 JUQ를 섞을 수는 없을 것 같은데, 질문은, 왜 그러고 싶냐는 것입니다.둘 다 다른 방법을 사용하고 있으니 하나만 선택하시면 됩니다.하나의 틀의 복잡성을 배우는 것은 충분히 어렵습니다.

Spring Data JPA는 다음을 제공합니다.

  1. 데이터베이스 테이블을 Java 개체처럼 처리할 수 있는 ORM 계층입니다.데이터베이스에 구애받지 않는 코드(MySQL, Oracle, SQL Server 등을 사용할 수 있음)를 작성할 수 있으며, 기본 SQL을 작성할 때 발생하는 오류 발생 가능성이 높은 코드의 대부분을 방지할 수 있습니다.
  2. 작업 단위 패턴입니다.C#에서 작업 단위가 무엇인지 설명하는 많은 기사를 보는 이유 중 하나는 JPA 때문입니다.자바는 이것을 15년 동안 가지고 있었습니다. C#, 글쎄요, 당신은 전혀 모릅니다.
  3. 도메인 기반 설계 저장소 DDD는 데이터베이스 기반 응용 프로그램에서 자주 볼 수 있는 빈혈 도메인 모델을 제거하는 개체 지향 소프트웨어에 대한 접근 방식으로, 엔티티 개체에는 데이터와 액세스 방법(애너미 모델)만 있고 서비스 클래스의 모든 비즈니스 로직이 있습니다.더 많은 것들이 있지만, 이것은 Spring Data JPA와 관련된 가장 중요한 부분입니다.
  4. 통제의 역전, 의존성 주입 등을 통한 스프링 생태계로의 통합.

반면 jOOQ는 활성 레코드 패턴을 구현하는 데이터베이스 매핑 라이브러리입니다.데이터베이스 운영에 SQL 중심적인 접근 방식을 사용하며, 이를 위해 도메인별 언어를 사용합니다.

자주 있는 일이지만, 정확하거나 우수한 선택은 없습니다.Spring Data JPA는 데이터베이스에 관심이 없는 경우 매우 잘 작동합니다.복잡한 쿼리를 수행하지 않으신다면 Spring Data JPA로 충분합니다.그러나 테이블 간에 조인을 수행해야 하는 경우 Spring Data JPA 저장소가 특정 작업에 적합하지 않다는 것을 알게 됩니다.

@michael-simons가 언급했듯이, 이 두 가지를 결합하는 것이 때때로 최고의 해결책이 될 수 있습니다.

JUQ가 적합한 경우의 공식 설명은 다음과 같습니다.

https://www.jooq.org/doc/latest/manual/getting-started/jooq-and-jpa/

jOOQ를 사용한다고 해서 모든 것에 사용해야 하는 것은 아닙니다!

JPA를 사용하는 기존 애플리케이션에 jOOQ를 도입할 때 항상 공통적인 질문은 "JOOQ로 JPA를 대체해야 할까요?"와 "어떻게 해야 할까요?"입니다.

jOOQ는 JPA를 대체하는 것이 아닙니다.jOOQ를 보어로 생각합니다. JPA(그리고 일반적으로 ORM)는 객체 그래프 지속성 문제를 해결하려고 합니다.간단히 말해서, 이 문제는

데이터베이스에서 엔티티 그래프를 클라이언트 메모리로 로드 클라이언트에서 해당 그래프 조작 수정 사항을 데이터베이스에 다시 저장 위의 그래프가 복잡해짐에 따라 다음과 같은 까다로운 질문이 많이 발생합니다.

엔티티 로드 및 저장을 위한 SQL DML 작업의 최적 순서는 무엇입니까?어떻게 하면 명령을 더 효율적으로 배치할 수 있습니까?어떻게 하면 ACID를 손상시키지 않고 트랜잭션 설치 공간을 최대한 낮게 유지할 수 있습니까?어떻게 낙관적인 잠금을 구현할 수 있습니까?jOOQ는 일부 답만 가지고 있습니다.jOOQ는 간단한 CRUD, 배치 API, 낙관적 잠금 기능을 실행하는 데 도움이 되는 업데이트 가능한 레코드를 제공하지만 jOOQ는 주로 실제 SQL 문 실행에 중점을 둡니다.

SQL은 다음 중 하나가 제공되는 경우 데이터베이스 상호 작용의 기본 언어입니다.

대용량 데이터 세트에 대한 보고서 및 분석을 데이터베이스에서 직접 실행 ETL을 사용하여 데이터 가져오기/내보내기 복잡한 비즈니스 로직을 SQL 쿼리로 실행 SQL이 적합할 때마다 jOOQ가 적합합니다.객체 그래프를 조작하고 유지할 때마다 JPA가 적합합니다.

그리고 가끔은 두 가지를 결합하는 것이 가장 좋습니다.

Spring Data JPA는 기본 쿼리를 실행하는 기능(설정을 통해)을 가진 @Queryidom을 지원합니다.nativeQueryflag)를 사용하면 저장소와 함께 쿼리를 작성하고 확인할 수 있습니다(결합 또는 기타 방식으로 쿼리를 쉽게 재사용할 수 있습니다.

위의 내용을 고려하면,

JUQ와 함께 Spring Data JPA over Spring Boot를 사용하거나 그 반대의 경우는 언제입니까?

스프링 생태계 자체를 사용하지 않는 한 스프링 데이터 JPA를 사용할 것입니다.또 다른 이유는 제가 유창한 스타일을 선호하기 때문일 것입니다.

스프링 데이터 JPA가 기본 CRUD 쿼리를 완료하는 데 사용될 수 있다는 것은 알고 있지만 복잡한 조인 쿼리에는 사용되지 않습니다.

위에서 언급했듯이 Spring Data JPA는 복잡한 쿼리를 사용하거나 쿼리를 조인할 수 있는 기능을 제공합니다.또한 사용자 지정 저장소 메커니즘(예: 위의 @Michael Simons 게시물에서 JUQ를 사용)을 통해 훨씬 더 많은 유연성을 제공합니다.그래서 그것은 그 자체로 본격적인 해결책입니다.

당신은 jooq와 함께 spring data jpa를 모두 사용할 수 있습니까?

위의 @Michael Simons에 의해 이미 멋지게 대답했습니다.

언급URL : https://stackoverflow.com/questions/62055237/technical-difference-between-spring-boot-with-jooq-and-spring-data-jpa

반응형