Техническая разница между Spring Boot с JOOQ и Spring Data JPA - PullRequest
10 голосов
/ 28 мая 2020

Когда использовать Spring Data JPA вместо Spring Boot с JOOQ и наоборот?

Я знаю, что Spring Data JPA можно использовать для выполнения базовых c CRUD-запросов, но не совсем для сложных запросов на соединение при использовании JOOQ это упрощает?

РЕДАКТИРОВАТЬ: Можете ли вы использовать оба Spring data jpa с jooq?

Ответы [ 2 ]

7 голосов
/ 29 мая 2020

На ваш вопрос нет простого ответа. Я сделал пару докладов об этом топи c. Иногда есть веские причины иметь и то, и другое в проекте.

Edit: IMHO Абстракция над базой данных в отношении диалектов и типов данных здесь не главное !! jOOQ отлично справляется с генерацией SQL для заданного целевого диалекта - и JPA / Hibernate тоже. Я бы даже сказал, что jOOQ делает все возможное, чтобы имитировать функции для баз данных, которые не имеют всех наворотов, таких как Postgres или Oracle. Здесь возникает вопрос: «Я хочу иметь возможность express запроса со всем, что может предложить SQL, или я доволен тем, что JPA может express?»

Вот пример совместной работы обоих. У меня есть репозиторий Spring Data JPA с настраиваемым расширением (необходимы интерфейс и реализация). Я позволяю контексту Spring вводить как JPA EntityManager, так и контекст jOOQ. Затем я использую 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.

7 голосов
/ 29 мая 2020

ИМХО, если вам нужно работающее и удобное в обслуживании приложение, которое использует базу данных в своей основе, вы не хотите абстрагироваться от того факта, что вы используете базу данных. JOOQ дает вам полный контроль, потому что вы можете читать и писать фактический запрос в своем коде, но с безопасностью типов.

JPA охватывает объектно-ориентированную модель, и это просто не соответствует тому, как работает база данных во всех случаях, что может приведет к неожиданным запросам, например N + 1, потому что вы поместили неправильную аннотацию в поле. Если вы не уделяете достаточно внимания, это приведет к проблемам с производительностью при масштабировании приложения. JPA Criteria немного помогает, но его все еще сложнее писать и читать.

В результате с JPA вы сначала пишете свой запрос в SQL, а затем используете полдня, чтобы перевести его в критерии. После многих лет работы с обеими фреймворками я бы использовал JOOQ даже для простого приложения CRUD (потому что не существует такой вещи, как простое приложение CRUD: -)).

Изменить: я не думаю, что вы можете смешать JPA с JOOQ, вопрос в том, зачем вам это нужно? Они оба используют разные подходы, поэтому просто выберите один. Достаточно сложно изучить тонкости одного фреймворка.

...