spring - Spring Boot with JOOQ 和 Spring Data JPA 之间的技术差异

标签 spring spring-boot spring-data-jpa spring-data jooq

您何时将 Spring Data JPA 与 Spring Boot 与 JOOQ 一起使用,反之亦然?

我知道 Spring Data JPA 可用于完成基本的 CRUD 查询,但不能真正用于复杂的连接查询,而使用 JOOQ 使其更容易?

编辑:您可以将 Spring 数据 jpa 与 jooq 一起使用吗?

最佳答案

你的问题没有简单的答案。我已经就该主题进行了几次会谈。有时有充分的理由在一个项目中同时拥有这两者。

编辑:恕我直言,关于方言和数据类型的数据库抽象不是这里的重点!! jOOQ 在为给定的目标方言生成 SQL 方面做得非常好——JPA/Hibernate 也是如此。我什至会说 jOOQ 在模拟没有像 Postgres 或 Oracle 这样的所有花里胡哨的数据库的功能方面付出了更多努力。
这里的问题是“我是否希望能够用 SQL 必须提供的所有内容自己表达查询,还是我对 JPA 可以表达的内容感到满意?”

这是一个同时运行两者的示例。我在这里有一个 Spring Data JPA 提供的存储库,带有一个自定义扩展(接口(interface)和实现是必要的)。我让 Spring 上下文同时注入(inject) 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();
    }
}

我曾多次谈到这一点。这些技术中没有 Elixir ,有时这是一个非常薄弱的​​判断:

完整的演讲在这里: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 .

关于spring - Spring Boot with JOOQ 和 Spring Data JPA 之间的技术差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62055237/

相关文章:

html - 将 css 类添加到输入标签 thymeleaf + spring

spring - 找不到构造函数的参数 0 - Spring Boot 和 Tomcat

java - 通过同一个 ActiveMQConnectionFactory 使用多个 ActiveMQ 队列

java - 如何使用 spring boot 2 以编程方式获取所有执行器端点?

java - 响应式(Reactive) Redis 序列化问题。无法序列化类型类 java.lang.Integer 的值,而不在 Redis 中保存时生成序列化器错误

java - Spring Data Jpa - 如何执行回滚?

hibernate - 在 Hibernate 中使用 @Query 进行带参数的 native 查询

java - 在spring mvc框架中使用Spring加载

java - 与 ribbon/eureka 一起使用时假装客户端错误

java - 服务层中的验证(Spring Boot)