几天前,一位同事提出了以下问题:Order and group by with one column
已解决。一个简单干净的查询,非常酷。该解决方案似乎是值得的,除了我们需要在JPA中实现它。
由于JPA在JOIN中不接受子查询,因此我们必须将其作为nativeQuery进行,但是这样做会遇到分页问题,因为JPA不会将其与本地查询结合在一起。 https://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/
本地查询@Query批注允许执行本地查询 通过将nativeQuery标志设置为true。请注意,我们目前还没有 支持对本机查询执行分页或动态排序 因为我们必须处理声明的实际查询,所以我们无法做 对于本机SQL来说确实如此。
我们不知道该如何继续。
我们必须做的:我们拥有一系列包含电话号码,用户和日期的记录,每个用户已经可以拨打N次。我们需要获取按电话号码分组并按每组号码的最新日期进行排序(DESC)的所有记录。
例如:
使用此数据:
+--------------+---------------------+-------------+---------------+
| phone_number | registered | name | first_surname |
+--------------+---------------------+-------------+---------------+
| 222005001 | 2019-05-10 10:01:01 | Alvaro | Garcia |
| 222004001 | 2019-05-13 16:14:21 | David | Garcia |
| 111003001 | 2019-05-13 16:14:43 | Roberto | Martin |
| 111001000 | 2019-05-13 16:14:50 | Juan Manuel | Martin |
| 111001000 | 2019-05-13 16:14:50 | Maria | Alonso |
| 111001000 | 2019-05-13 16:14:50 | Roberto | Martin |
| 333006001 | 2019-05-13 16:14:55 | Benito | Lopera |
| 123456789 | 2019-05-13 16:15:00 | NULL | NULL |
| 987654321 | 2019-05-13 16:15:08 | NULL | NULL |
| 123456789 | 2019-05-13 16:15:13 | NULL | NULL |
| 666999666 | 2019-05-13 16:15:18 | NULL | NULL |
| 454545458 | 2019-05-13 16:15:27 | NULL | NULL |
| 333006001 | 2019-05-13 16:23:36 | Benito | Lopera |
| 987654321 | 2019-05-13 16:23:46 | NULL | NULL |
| 666999666 | 2019-05-13 16:23:50 | NULL | NULL |
| 454545458 | 2019-05-13 16:23:55 | NULL | NULL |
| 666999666 | 2019-05-13 16:24:03 | NULL | NULL |
| 222004001 | 2019-05-13 16:24:10 | David | Garcia |
+--------------+---------------------+-------------+---------------+
像这样对它们排序:
+--------------+---------------------+-------------+---------------+
| phone_number | registered | name | first_surname |
+--------------+---------------------+-------------+---------------+
| 222004001 | 2019-05-13 16:24:10 | David | Garcia |
| 222004001 | 2019-05-13 16:14:21 | David | Garcia |
| 666999666 | 2019-05-13 16:24:03 | NULL | NULL |
| 666999666 | 2019-05-13 16:23:50 | NULL | NULL |
| 666999666 | 2019-05-13 16:15:18 | NULL | NULL |
| 454545458 | 2019-05-13 16:23:55 | NULL | NULL |
| 454545458 | 2019-05-13 16:15:27 | NULL | NULL |
| 987654321 | 2019-05-13 16:23:46 | NULL | NULL |
| 987654321 | 2019-05-13 16:15:08 | NULL | NULL |
| 333006001 | 2019-05-13 16:23:36 | Benito | Lopera |
| 333006001 | 2019-05-13 16:14:55 | Benito | Lopera |
| 123456789 | 2019-05-13 16:15:13 | NULL | NULL |
| 123456789 | 2019-05-13 16:15:00 | NULL | NULL |
| 111001000 | 2019-05-13 16:14:50 | Maria | Alonso |
| 111001000 | 2019-05-13 16:14:50 | Roberto | Martin |
| 111001000 | 2019-05-13 16:14:50 | Juan Manuel | Martin |
| 111003001 | 2019-05-13 16:14:43 | Roberto | Martin |
| 222005001 | 2019-05-10 10:01:01 | Alvaro | Garcia |
+--------------+---------------------+-------------+---------------+
可以通过以下查询完成:
SELECT c.phone_number, c.registered, cl.name, cl.first_surname
FROM callers cl
INNER JOIN callers_phones cp ON cl.caller_id = cp.caller_id
RIGHT OUTER JOIN calls c ON c.phone_number = cp.phone_number
JOIN (
SELECT phone_number, MAX(registered) AS registered
FROM calls
GROUP BY phone_number) aux_c ON aux_c.phone_number = c.phone_number
WHERE c.answered = FALSE
AND (null is null or null is null or c.registered between null and null)
AND (null is null or c.phone_number = null)
AND (null is null or cl.caller_id = null)
ORDER BY aux_c.registered DESC, c.registered DESC
这些是表格:
CREATE TABLE callers
(
caller_id int NOT NULL UNIQUE AUTO_INCREMENT,
name varchar(50) NOT NULL,
first_surname varchar(50) NOT NULL,
CONSTRAINT callers_pkey PRIMARY KEY (caller_id)
);
CREATE TABLE callers_phones
(
phone_id int NOT NULL UNIQUE AUTO_INCREMENT,
caller_id int NOT NULL,
phone_number int NOT NULL,
CONSTRAINT callers_phones_pkey PRIMARY KEY (phone_id)
);
ALTER TABLE callers_phones
ADD CONSTRAINT callers_phones_fkey_callers FOREIGN KEY (caller_id)
REFERENCES callers (caller_id);
CREATE TABLE calls
(
call_id int NOT NULL UNIQUE AUTO_INCREMENT,
phone_number int NOT NULL,
answered boolean NOT NULL DEFAULT false,
registered datetime NOT NULL,
CONSTRAINT calls_pkey PRIMARY KEY (call_id)
);
问题在于我们必须在JPA中使用分页来实现它,但是子查询在JOIN子句中不起作用,分页在nativeQuery中不起作用。
这是我们所做的:
@Entity:
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.EntityResult;
import javax.persistence.FieldResult;
import javax.persistence.Id;
import javax.persistence.NamedNativeQuery;
import javax.persistence.SqlResultSetMapping;
@SqlResultSetMapping (name = "MissedCallResult",
entities = {
@EntityResult (entityClass = MissedCallEntity.class,
fields = {
@FieldResult (name = "callId", column = "id"),
@FieldResult (name = "phoneNumber", column = "pH"),
@FieldResult (name = "registered", column = "reg"),
@FieldResult (name = "callerName", column = "cN"),
@FieldResult (name = "callerFirstSurname", column = "cFS")
})
})
@NamedNativeQuery (name = "findMissedCalls",
query = "select c.call_id as id, c.phone_number as pH, c.registered as reg, cl.name as cN, cl.first_surname as cFS "
+ "from callers cl "
+ " inner join callers_phones cp on cl.caller_id = cp.caller_id "
+ " right outer join calls c on c.phone_number = cp.phone_number "
+ " join (select c2.phone_number, MAX(c2.registered) as registered "
+ " from calls c2 "
+ " group by c2.phone_number) aux_c on aux_c.phone_number = c.phone_number "
+ "where c.answered = false "
+ " and (:startDate is null or :endDate is null or c.registered between :startDate and :endDate) "
+ " and (:callerId is null or cl.caller_id = :callerId) "
+ " and (:phoneNumber is null or c.phone_number = :phoneNumber) "
+ "order by aux_c.registered desc, c.registered desc",
resultSetMapping = "MissedCallResult")
@Entity
public class MissedCallEntity
{
@Id
private Integer callId;
private Integer phoneNumber;
private Date registered;
private String callerName;
private String callerFirstSurname;
private String callerSecondSurname;
...
}
@存储库:
import java.util.Date;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import es.panel.domain.MissedCallEntity;
@RepositoryRestResource (path = "missedCalls", collectionResourceRel = "missedCalls")
public interface MissedCallRepository extends PagingAndSortingRepository<MissedCallEntity, Integer>
{
@Query (nativeQuery = true, name = "findMissedCalls")
Page<MissedCallEntity> findMissedCalls(@Param ("startDate") Date startDate,
@Param ("endDate") Date endDate,
@Param ("callerId") Integer callerId,
@Param ("phoneNumber") Integer phoneNumber,
Pageable page);
}
在@Service中:
public Page<MissedCallEntity> getMissedCalls(Date startDate,
Date endDate,
Integer callerId,
Integer phoneNumber,
int actualPage,
int limit)
{
Page<MissedCallEntity> calls = mcRepository.findMissedCalls(
startDate, endDate, callerId, phoneNumber, PageRequest.of(1, 5));
return calls;
}
谢谢!
答案 0 :(得分:0)
一个非常简单的解决方案是根据您的查询创建一个数据库视图,以获取计算值,即计数和最大值等。
您可以使用JPA {{1}}批注将其映射到相关实体,该批注使您可以将实体映射到多个表(或视图)。
在这里,您可以像使用其他任何字段一样使用标准JPA / spring数据功能进行排序和过滤,并且几乎可以删除所有已编写的代码。
我将进一步详细说明,但是您要达到的目标还不是很清楚:您在询问的是您尝试的解决方案,而不是问题本身。 MissedCall也不是实体。系统中的实体是用户,呼叫,电话等。