【发布时间】:2019-10-03 20:29:50
【问题描述】:
前几天有同事提出这个问题: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。
这就是我们所做的:
@实体:
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;
...
}
@Repository:
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;
}
提前致谢!
【问题讨论】:
标签: mysql spring-data-jpa