Java 处理JPA期间查询的正确方法是什么?
我需要生成下面的预期结果。基本上,它是一个查询,根据特定的周期每周、每月等聚合值。有一个日期过滤器,有开始和结束,我们需要返回所有范围的值。如果它们不存在,则应返回0 在下例中,开始日期为“2015-08-02”,日期为“2015-08-23”,期间为每周。请注意,对于第2周,我们没有值,但应该返回一个零值 那么,在这种情况下,使用JPA实现这一点的最佳方法是什么?我们考虑使用临时表并将结果与此表连接以获得整个范围的结果,但我不知道使用JPA是否可行,因为我们需要创建表、连接然后销毁临时表 另一个选项是创建数据库视图并将其映射到实体 在上述情况下,JPQL查询应该是这样的:Java 处理JPA期间查询的正确方法是什么?,java,postgresql,hibernate,jpa,Java,Postgresql,Hibernate,Jpa,我需要生成下面的预期结果。基本上,它是一个查询,根据特定的周期每周、每月等聚合值。有一个日期过滤器,有开始和结束,我们需要返回所有范围的值。如果它们不存在,则应返回0 在下例中,开始日期为“2015-08-02”,日期为“2015-08-23”,期间为每周。请注意,对于第2周,我们没有值,但应该返回一个零值 那么,在这种情况下,使用JPA实现这一点的最佳方法是什么?我们考虑使用临时表并将结果与此表连接以获得整个范围的结果,但我不知道使用JPA是否可行,因为我们需要创建表、连接然后销毁临时表 另一
@Query("select status, sum(totalInvoice), week from Invoice where " +
"left join TempTable as tt..." + <-- TEMP TABLE OR VIEW TO GET THE PERIODS
"issuer.id = :issuerId and type = :type and (:recipientId is null or recipient.id = :recipientId) and " +
"status in ('ISSUED', 'PAID') " +
"group by status")
另一种选择是使用存储过程,但它们似乎很难用JPA实现,我认为它们不是必需的
预期结果:
{
"code":"xxx",
"title":"This is the title of the first series"
"type":"PERIODIC",
"period":"WEEKLY", <-- PERIOD
"from":"2015-08-02",
"to":"2015-08-29",
"labels": ["2015-08-02", "2015-08-09", "2015-08-16", "2015-08-23"],
"tabelType": "TEXT",
"series":[
{
"code":"xxx",
"title":"This is the title of the first series"
"values":[10, 0, 13, 18] <- in this example, we don't have values for label "2015-08-09"
},
{
"code":"xxx",
"title":"This is the title of the second series"
"values":[10, 0, 13, 18] <- in this example, we don't have values for label "2015-08-09"
}
]
}
这可能不是您问题的直接答案,但是:为什么您需要直接在JPA查询中进行分组,而不是在Java代码中进行分组?这种类型的复杂语义分组在使用Java时做得非常好,但SQL数据库通常不擅长生成这种类型的结构化数据。如果没有其他理由在数据库级别执行此操作,例如,您需要一个填充了此数据的视图,该视图可根据时间段进行搜索,然后,只需加载原始数据并使用Java代码填充结构-这样会大大减少编码开销,并且可能会更高效。这可能不是您问题的直接答案,但是:为什么您需要直接在JPA查询中而不是在Java代码中进行分组?这种类型的复杂语义分组在使用Java时做得非常好,但SQL数据库通常不擅长生成这种类型的结构化数据。如果没有其他理由在数据库级别上这样做,比如说,您需要一个填充了该数据的视图,该视图可以基于句点进行搜索,然后只需加载原始数据并使用Java代码填充结构-它将大大减少编码开销,并且可能会更高效。@pozs在这里提供了答案。这只能通过本机查询PostgreSQL完成。结果如下:
/**
* Returns the counts, totals and averages of the states by their currency, period and status.
*/
@Query(value = "select i.currency, date(p), i.status, count(id), sum(coalesce(i.total, 0)), avg(coalesce(i.total, 0)) " +
"from generate_series(date_trunc(:period, cast(:from as timestamp)), date_trunc(:period, cast(:to as timestamp)) + cast('1 ' || :period as interval), cast('1 ' || :period as interval)) p " +
"inner join invoice i on i.due_date >= p and i.due_date < p + cast('1 ' || :period as interval) " +
"where issuer_id = :issuerId and type = :type and (:recipientId = 0 or recipient_id = :recipientId) and type = :type " +
"group by i.currency, date(p), i.status " +
"order by i.currency, date(p), i.status", nativeQuery = true)
List<Object[]> getIssuerStatementTotalsByCurrencyPeriodAndStatus(
@Param("issuerId") long issuerId,
@Param("recipientId") long recipientId,
@Param("type") String statementType,
@Param("from") String from,
@Param("to") String to,
@Param("period") String period);
请注意,这将返回对象数组的列表。还要注意,我无法将枚举和复杂参数传递到方法中。我不得不将这些值简化为字符串和原语
我已经通过以下课程将此结果转化为有意义的内容:
/**
* Contains the result of a single result in an aggregate query.
*/
public class AggregateResult {
private List<String> keys;
private List<BigDecimal> values;
@SuppressWarnings("unused")
public AggregateResult(Object value1, Object value2) {
this(new Object[] { value1, value2 });
}
@SuppressWarnings("unused")
public AggregateResult(Object value1, Object value2, Object value3) {
this(new Object[] { value1, value2, value3 });
}
@SuppressWarnings("unused")
public AggregateResult(Object value1, Object value2, Object value3, Object value4) {
this(new Object[] { value1, value2, value3, value4 });
}
@SuppressWarnings("unused")
public AggregateResult(Object value1, Object value2, Object value3, Object value4, Object value5) {
this(new Object[] { value1, value2, value3, value4, value5 });
}
public AggregateResult(Object... vals) {
values = new ArrayList<>();
while (values.size() < vals.length && vals[vals.length - values.size() - 1] instanceof Number) {
Number number = (Number) vals[vals.length - values.size() - 1];
values.add(number instanceof BigDecimal ? (BigDecimal) number : new BigDecimal(number.toString()));
}
this.keys = Stream.of(ArrayUtils.subarray(vals, 0, vals.length - values.size())).map(Object::toString).collect(toList());
}
public List<String> getKeys() {
return keys;
}
public List<BigDecimal> getValues() {
return values;
}
/**
* Returns the list of {@link AggregateResult}s for the raw result. The raw result is expected to
* have been returned from a native JPA query.
*/
public static List<AggregateResult> fromNativeResult(List<Object[]> raw) {
return raw.stream().map(AggregateResult::new).collect(toList());
}
}
@pozs在这里提供了答案。这只能通过本机查询PostgreSQL完成。结果如下:
/**
* Returns the counts, totals and averages of the states by their currency, period and status.
*/
@Query(value = "select i.currency, date(p), i.status, count(id), sum(coalesce(i.total, 0)), avg(coalesce(i.total, 0)) " +
"from generate_series(date_trunc(:period, cast(:from as timestamp)), date_trunc(:period, cast(:to as timestamp)) + cast('1 ' || :period as interval), cast('1 ' || :period as interval)) p " +
"inner join invoice i on i.due_date >= p and i.due_date < p + cast('1 ' || :period as interval) " +
"where issuer_id = :issuerId and type = :type and (:recipientId = 0 or recipient_id = :recipientId) and type = :type " +
"group by i.currency, date(p), i.status " +
"order by i.currency, date(p), i.status", nativeQuery = true)
List<Object[]> getIssuerStatementTotalsByCurrencyPeriodAndStatus(
@Param("issuerId") long issuerId,
@Param("recipientId") long recipientId,
@Param("type") String statementType,
@Param("from") String from,
@Param("to") String to,
@Param("period") String period);
请注意,这将返回对象数组的列表。还要注意,我无法将枚举和复杂参数传递到方法中。我不得不将这些值简化为字符串和原语
我已经通过以下课程将此结果转化为有意义的内容:
/**
* Contains the result of a single result in an aggregate query.
*/
public class AggregateResult {
private List<String> keys;
private List<BigDecimal> values;
@SuppressWarnings("unused")
public AggregateResult(Object value1, Object value2) {
this(new Object[] { value1, value2 });
}
@SuppressWarnings("unused")
public AggregateResult(Object value1, Object value2, Object value3) {
this(new Object[] { value1, value2, value3 });
}
@SuppressWarnings("unused")
public AggregateResult(Object value1, Object value2, Object value3, Object value4) {
this(new Object[] { value1, value2, value3, value4 });
}
@SuppressWarnings("unused")
public AggregateResult(Object value1, Object value2, Object value3, Object value4, Object value5) {
this(new Object[] { value1, value2, value3, value4, value5 });
}
public AggregateResult(Object... vals) {
values = new ArrayList<>();
while (values.size() < vals.length && vals[vals.length - values.size() - 1] instanceof Number) {
Number number = (Number) vals[vals.length - values.size() - 1];
values.add(number instanceof BigDecimal ? (BigDecimal) number : new BigDecimal(number.toString()));
}
this.keys = Stream.of(ArrayUtils.subarray(vals, 0, vals.length - values.size())).map(Object::toString).collect(toList());
}
public List<String> getKeys() {
return keys;
}
public List<BigDecimal> getValues() {
return values;
}
/**
* Returns the list of {@link AggregateResult}s for the raw result. The raw result is expected to
* have been returned from a native JPA query.
*/
public static List<AggregateResult> fromNativeResult(List<Object[]> raw) {
return raw.stream().map(AggregateResult::new).collect(toList());
}
}
在Postgres中,这通常是用来完成的,但是除了使用本机查询之外,我想不出一种JPA友好的方式。@pozs,是的,如何在JPA上完成这项工作是最困难的部分。我在想,也许可以使用映射到JPA实体的视图,但我不知道是否可行。在Postgres中,这通常是用来完成的,但是除了使用本机查询之外,我想不出一种JPA友好的方式。@pozs,是的,如何在JPA上实现这一点是最困难的部分。我在想也许使用一个映射到JPA实体的视图,但我不知道它是否有效。谢谢你的回答,但我认为用Java代码做这类事情应该会慢一些。通常SQL的速度相当快。这完全取决于您想要执行的操作类型。然而,由于Java是在内存中运行的,SQL服务器通常除了缓存的查询/索引之外不会运行,因此经过良好编程的Java代码通常比其SQL等价物要快。然而,当涉及到各种类型的临时分组时,在大多数情况下,Java将比SQL更快。此外,您还必须记住代码清晰性和可管理性的潜在成本。即使数据库解决方案更快,问题是您是否愿意为更难阅读和维护的解决方案付出代价。谢谢您的回答,但我认为用Java代码来完成这类工作应该更慢。通常SQL的速度相当快。这完全取决于您想要执行的操作类型。然而,由于Java是在内存中运行的,SQL服务器通常除了缓存的查询/索引之外不会运行,因此经过良好编程的Java代码通常比其SQL等价物要快。然而,当涉及到各种类型的临时分组时,在大多数情况下,Java将比SQL更快。还有,y 您必须记住代码清晰性和可管理性的潜在成本。即使数据库解决方案更快,问题是您是否愿意为更难阅读和维护的解决方案付出代价。