我有以下Java记录:

record Info(Integer count){}
record Person(Integer id, String name) {}
record MyResult(Info info, List<Person> persons){}

我需要返回一个MyResult对象,其中包含Info中的人员总数和这些人员的部分列表.

我不知道是否可以只用一个JOOQ查询直接完成它,也不知道如何做到这一点.

这是一种try ,但显然它将获取一个列表,因此这不是正确的方式.

DSL.getContext(conn)
  .select(
    DSL.row(DSL.count().over()).mapping(Info::new),
    DSL.multiset(
      DSL.select(
         PERSON.PERSON_ID,
         PERSON.NAME
      ).convertFrom(r -> r.map(Records.mapping(PERSON::new)))
    )
  )
   .from(PERSON)
   .where(PERSON.NAME.startWith("A"))
   .orderBy(PERSON.NAME.asc())
   .offset(0)
   .limit(5) 
   .fetchOne(Records.mapping(MyResult::new));

有什么 idea 吗?

推荐答案

这里的问题是SQL's logical order of operations.对结果进行计数的任何手段(合计计数和窗口计数)都发生before,应用LIMIT.因此,如果您不希望LIMIT对您的计数产生任何影响,则必须以它首先计数的方式编写查询.但是,如果从WHERE子句中没有得到任何结果,那么在使用窗口计数时就不会得到任何行.当使用聚合计数时,您会得到一行,但是您不能再这么容易地应用LIMIT.(如果没有LIMIT,只需使用Aggregate Count和multisetAgg()即可).

因此,我可以看到两种解决方案,一种缓慢但简单,一种更快但更复杂:

执行查询中的所有操作,访问表两次:

如果您想要将所有映射逻辑移到查询本身中,我怀疑您必须对PERSON个表运行2个查询,出于性能原因,这可能不是我们所希望的:

ctx.select(
      count().filterWhere(PERSON.NAME.startsWith("A")).convertFrom(Info::new),
      multiset(
        select(PERSON_ID, PERSON_NAME)
        .from(PERSON)
        .where(PERSON.NAME.startsWith("A"))
        .orderBy(PERSON.NAME.asc())
        .limit(5)
      ).convertFrom(r -> r.map(mapping(Person::new)))
    )
   .from(PERSON)
   .fetchOne(mapping(MyResult::new))

这适用于您的简单示例,但是一旦开始连接事物,您就必须在内部查询和外部查询之间复制大量查询逻辑,这并不是真正需要的.

Using JDK Collector API

但是您不必像您的目标数据 struct 那样以分层的方式形成查询.在jOOQ之外,您仍然可以在客户端执行some个映射逻辑,例如:

ctx.select(
      count().over().convertFrom(Info::new),
      row(PERSON_ID, PERSON_NAME).mapping(Person::new)
    )
    .from(PERSON)
    .where(PERSON.NAME.startsWith("A"))
    .orderBy(PERSON.NAME.asc())
    .limit(5)
    .collect(collectingAndThen(
        groupingBy(
            // There's a single possible value from count().over()
            r -> r.value1(),
            mapping(r -> r.value2(), toList())
        ),
        m -> {
            // Probably, just use var here...
            Iterator<Entry<Info, List<Person>>> it = m.entrySet().iterator();

            if (it.hasNext()) {
                Entry<Info, List<Person>> e = it.next();
                return new MyResult(e.getKey(), e.getValue());
            }
            else
                return new MyResult(new Info(0), Collections.emptyList());
        }
    )));

这假设有以下静态导入:

import static java.util.stream.Collectors.*;

诚然,它并不是那么漂亮.您可以将其重构为不同的格式,或者为这种结果编写一些更通用的实用程序,但我希望您能领会其中的精髓.

Note:如果您翻页到最后一页之外,这种方法不会给出正确的计数值

Java相关问答推荐

如何在Inspaut中获取当前路径的 * 模式 *?

Android视图覆盖不阻止点击它后面的控件

无法处理批处理侦听器中的反序列化异常

扩展到弹出窗口宽度的JavaFX文本字段

FALSE:它应该在什么时候使用?

为什么在maven中,getLast方法不适用于List?

@Rollback @ Transmission在验收测试中不工作

在Eclipse中数组的可空性

用OSQL创建索引

在Java中将int[]矩阵添加到ArrayList中,但出现错误

如何使用MapStrCut转换双向链接

如何在EL处理器中定义带有命名空间的变量?

AWS Java SDK v2.x中没有setObjectAcl方法

在应用程序运行时更改LookAndFeel

如何制作回文程序?

根据应用程序 Select 的语言检索数据

如何在单元测试中获得我的装饰Mapstruct映射器的实例?

获取401未经授权,即使在标头中设置了浏览器名称和cookie

使用StringBuilder和append方法创建字符串时Java字符串内部方法的问题

错误:JOIN/ON的参数必须是boolean类型,而不是bigint类型.Java Spring启动应用程序