为 oneTomany 基于关系的查询编写 queryDSL 谓词查询

Posted

技术标签:

【中文标题】为 oneTomany 基于关系的查询编写 queryDSL 谓词查询【英文标题】:Write queryDSL predicate query for oneTomany relation based query 【发布时间】:2017-03-15 17:16:20 【问题描述】:

我正在使用 spring-data、QueryDSL 和 mysql

问题的主要目的是知道如何以 queryDSL 方式进行这样的查询。给出的例子只是一个简单的例子来给出想法。

例如,有两个表 Employee 和 Certificate。两者之间的关系是 ONE(员工)到 MANY(证书)

以下是表格,

Employee (id, first_name, last_name);
Certificate (id, name, date, fk_emp);

QueryDSL 谓词应该是什么

返回所有姓名包含(在 first_name 和 last_name 中)的员工,并从该结果中返回其认证日期在 2014 年 22 月 12 日至 2015 年 22 月 12 日之间的员工

我试过了,但不知道如何以 QueryDSL 方式遍历每个员工的每个证书并返回员工列表。

我们将非常感谢您的回复!

编辑

以下是实体,

@Entity
@Table(name = "EMPLOYEE")
class Employee 
  @Id
  @Column(name = "ID")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(name = "FIRST_NAME")
  private String firstName;

  @Column(name = "LAST_NAME")
  private String lastName;

  @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL)
  private List<Certificate> certificates = new ArrayList<>();


@Entity
@Table(name = "CERTIFICATE")
class Certificate 
  @Id
  @Column(name = "ID")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(name = "CERTIFICATE_NAME")
  private String certificateName;

  @Column(name = "DATE")
  private Date date;

  @ManyToOne
  @JoinColumn(name = "REF_EMPLOYEE")
  private Employee employee;

【问题讨论】:

查看我的更新答案。生成的 SQL 非常丑陋,但看起来可以正常工作。 【参考方案1】:

最简单的方法是反转它并查询证书,这很简单,然后您可以从返回的证书中获取员工。

QCertificate certificate = QCertificate.certificate;
BooleanExpression a = certificate.date.between(d1, d2);
BooleanExpression b = certificate.employee.forename.eq("name").
      or(certificate.employee.surname.eq("name"));

certificateRepository.findAll(a.and(b));

如果您想查询员工,请尝试以下针对 QueryDSL 版本 4.1.3 的操作。

    QEmployee employee = QEmployee.employee;
    QCertificate certificate = QCertificate.certificate;

    BooleanExpression a = employee.forename.eq("name").or(employee.surname.eq("name"));

    BooleanExpression b = employee.certificates.contains(
        JPAExpressions.selectFrom(certificate).
          where(certificate.employee.eq(employee).
           and(certificate.date.between(d1, d2))));

    userRepository.findAll(a.and(b));

【讨论】:

此解决方案不起作用并引发以下异常 org.springframework.dao.InvalidDataAccessApiUsageException:未声明的路径“证书”。将此路径作为源添加到查询中,以便能够引用它。嵌套异常是 java.lang.IllegalArgumentException: Undeclared path 'certificate.将此路径作为源添加到查询中,以便能够引用它。 正如我在回答“在没有任何代码的情况下,我建议它看起来像”中所指出的那样。您需要使其适合您的实体映射,因为这里没有人可以看到它们。 我添加了实体。请参阅问题的编辑部分。如果您需要更多信息,请告诉我 我不明白您的回答中“遵循 QueryDSL 版本 4.1.3”的含义。请你详细说明一下。 ?使用了 4.1.3 版本的库。【参考方案2】:

由于这是使用 MySQL 标记发布的,因此我将回答您(为其他人)需要的查询,然后希望您能从中找出 QueryDSL 代码:

SELECT * from Certificate 
    WHERE date > "2014-01-01 00:00:00" and date < "2015-01-01 00:00:00" AND 
    id IN (SELECT id from Employee 
           WHERE first_name LIKE '%Name%' 
                 || last_name LIKE '%LName%')

从命令行输出:

mysql> SELECT * from Certificate 
    ->         WHERE date > "2014-01-01 00:00:00" and date < "2015-01-01 00:00:00" AND 
    ->         id IN (SELECT id from Employee 
    ->                WHERE first_name LIKE '%Name%' 
    ->                      || last_name LIKE '%LName%');
+----+--------------------+---------------------+--------+
| id | name               | date                | fk_emp |
+----+--------------------+---------------------+--------+
|  1 | FirstName LastName | 2014-02-01 00:00:00 |  11111 |
+----+--------------------+---------------------+--------+
1 row in set (0.00 sec)

抱歉,不熟悉 QueryDSL,因此您需要修改代码以匹配该查询。

这是我用来测试它的架构:

CREATE TABLE `Certificate` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `date` datetime NOT NULL,
  `fk_emp` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `Certificate` (`id`, `name`, `date`, `fk_emp`) VALUES
(1, 'FirstName LastName',   '2014-02-01 00:00:00',  11111);

CREATE TABLE `Employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(255) NOT NULL,
  `last_name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `Employee` (`id`, `first_name`, `last_name`) VALUES
(1, 'FirstName',    'LastName');

【讨论】:

以上是关于为 oneTomany 基于关系的查询编写 queryDSL 谓词查询的主要内容,如果未能解决你的问题,请参考以下文章

在 JHipster 的 @OneToMany JPA 关系中编写孩子的问题

用于从 OneToMany 关系中获取不同值的 MySQL 或 JPQL 查询(条件优先级)

不加入子表的 JPA (@OneToMany) 查询

不接收来自数据库的更新数据(Spring OneToMany 关系)

Spring JPA - @OneToMany 为每个关系创建单独的表

Spring JPA - @OneToMany为每个关系创建单独的表