【问题标题】:QueryDSL intersection with Spring Boot Data JPAQueryDSL 与 Spring Boot Data JPA 的交集
【发布时间】:2020-08-18 15:01:47
【问题描述】:

我在 Spring Boot、Spring Data JPA 项目中使用 QueryDSL。 我有一个名为test 的表的以下架构:

| id | key  | value |
|----|------|-------|
| 1  | test | hello |
| 1  | test | world |
| 2  | test | hello |
| 2  | foo  | bar   |
| 3  | test | hello |
| 3  | test | world |

现在我想在 QueryDSL 中编写以下 SQL:

select id from test where key = 'test' and value = 'hello'
INTERSECT
select id from test where key = 'test' and value = 'world'

这会给我所有的 ID,其中键是“测试”,值是“你好”和“世界”。

我还没有找到在 QueryDSL 中声明这种 SQL 的任何方法。我可以编写两个 select 语句,但后来我被困在将它们与 INTERSECT 结合起来。

JPAQueryFactory queryFactory = new JPAQueryFactory(em); // em is an EntityManager

QTestEntity qTestEntity = QTestEntity.testEntity;

var q1 = queryFactory.query().from(qTestEntity).select(qTestEntity.id).where(qTestEntity.key("test").and(qTestEntity.value.eq("hello")));
var q2 = queryFactory.query().from(qTestEntity).select(qTestEntity.id).where(qTestEntity.key("test").and(qTestEntity.value.eq("world")));;

最后,我想检索与给定查询匹配的 id 列表。一般来说,相交的数量可能在 20 或 30 左右,具体取决于我要搜索的键/值对的数量。

有谁知道如何用 QueryDSL 做这样的事情?

编辑:

现在假设以下架构,有两个表:test 和“用户”:

test:

| userId  | key  | value |
|---------|------|-------|
| 1       | test | hello |
| 1       | test | world |
| 2       | test | hello |
| 2       | foo  | bar   |
| 3       | test | hello |
| 3       | test | world |

user:

| id | name     |
|----|----------|
| 1  | John     |
| 2  | Anna     |
| 3  | Felicita |

对应的java类如下所示。 TestEntity 有一个由其所有属性组成的复合键。

@Entity
public class TestEntity {
    @Id
    @Column(name = "userId", nullable = false)
    private String pubmedId;

    @Id
    @Column(name = "value", nullable = false)
    private String value;

    @Id
    @Column(name = "key", nullable = false)
    private String key;
}

@Entity
class User {
  @Id 
  private int id;

  private String name;

  @ElementCollection
  private Set<TestEntity> keyValues;
}

如何将test 表映射到User 类中的keyValues 属性?

【问题讨论】:

    标签: java spring-boot spring-data-jpa querydsl


    【解决方案1】:

    在这种情况下,我可能只使用OR 表达式:

    queryFactory
        .query()
        .from(qTestEntity) .select(qTestEntity.id)
        .where(qTestEntity.key("test").and(
            qTestEntity.value.eq("hello")
           .or(qTestEntity.value.eq("world")));
    

    但是,您特别提到要使用集合操作。顺便说一句,我认为您想要执行 UNION 操作而不是 INSERSECT 操作,因为在给出的示例中后者将是空的。

    JPA 不支持 ANSI SQL 中定义的集合操作。但是,Blaze-Persistence 是与大多数 JPA 实现集成的扩展,并且确实通过集合操作扩展了 JPQL。我最近为 Blaze-Persistence 编写了一个 QueryDSL 扩展。使用该扩展,您可以:

    List<Document> documents = new BlazeJPAQuery<Document>(entityManager, cbf)
        .union(
             select(document).from(document).where(document.id.eq(41L)),
             select(document).from(document).where(document.id.eq(42L))
        ).fetch();
    

    有关集成及其设置方法的更多信息,请参阅https://persistence.blazebit.com/documentation/1.5/core/manual/en_US/index.html#querydsl-integration 上的文档

    【讨论】:

      【解决方案2】:

      您的 TestEntity 并不是真正的实体,因为它的 id 不是主键,而是用户表的外键。

      如果它只能通过使用它的所有属性来识别,它就是一个@Embeddable,并且没有任何@Id 属性。

      您可以将 Embeddables 的集合映射为另一个以 id 作为主键的实体的 @ElementCollection 部分。您的 id 列不是 Embeddable 的属性,它只是主表的外键,因此您将其映射为 @JoinColumn:

      @Embeddable
      public class TestEmbeddable {
      
          @Column(name = "value", nullable = false)
          private String value;
      
          @Column(name = "key", nullable = false)
          private String key;
      }
      
      @Entity
      class User {
        @Id 
        private int id;
        @ElementCollection
        @CollectionTable(
           name="test",
           joinColumns=@JoinColumn(name="id")
        )
        private Set<TestEmbeddable> keyValues;
      }
      

      在这种情况下,QueryDSL 变成了这样(不知道确切的 api):

      user.keyValues.any().in(new TestEmbeddable("test", "hello"))
        .and(user.keyValues.keyValues.any().in(new TestEmbeddable("test", "world"))
      

      【讨论】:

      • 你确实是对的。 QTestEntity 是另一个实体的一部分。根据您的建议,QueryDSL 确实变得更加清晰。我现在正在努力寻找将TestEntity 映射到Users 类的方法。我对我的原始帖子进行了编辑。我遇到的问题是TestEntity 有一个由其所有属性组成的复合键,但我只需要将userIduserid 匹配。有没有办法做到这一点?
      • 我更新了答案。我不确定 QueryDSL 是否像那样工作,或者您需要对 .key("hello") 和 .value("world") 做一些事情。
      猜你喜欢
      • 2017-09-22
      • 2020-06-16
      • 2020-01-18
      • 2017-05-09
      • 2012-11-09
      • 1970-01-01
      • 2017-09-02
      • 2016-02-17
      • 2014-03-29
      相关资源
      最近更新 更多