数据表简介:
该数据表用于记录某个设备发送的数据。在该表中,有大量重复的数据,因此,无论给哪个字段添加主键,或者设置联合主键都是不可行的。
code | createTime | equipNum | reqTime | wight |
---|---|---|---|---|
8301 | 1629788252000 | SNU234897 | 2021-08-24 15:14:29 | 30 |
8301 | 1629788261000 | SNU248093 | 2021-08-24 15:22:03 | 22 |
8301 | 1629787680000 | SNU234897 | 2021-08-24 15:18:18 | 32 |
8305 | 1629787680000 | GBY298456 | 2021-08-24 15:20:02 | 30 |
8305 | 1629795000000 | GBY298456 | 2021-08-24 17:18:01 | 30 |
8302 | 1629794700000 | GBY298456 | 2021-08-24 17:20:02 | 30 |
8301 | 1629794700000 | SNU234897 | 2021-08-24 17:16:02 | 32 |
8301 | 1629794700000 | GBY298456 | 2021-08-24 15:18:02 | 30 |
而Jpa强制实体类中需要有主键。所以就很随便的选了第一个字段,在它上边加了注解@Id
实体类
@Entity
@Table(name = "equip")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Equip{
@Id
@Column(name = "code")
private String code;
@Column(name = "createTime")
private String createTime;
@Column(name = "equipNum")
private String equipNum;
@Column(name = "reqTime")
private String reqTime;
@Column(name = "wight")
private String wight;
}
业务场景需求:
查询特定时间段内,某个设备(equipNum)最新(createTime)的那条记录。
实现思路:
按createTime范围进行筛选,之后按设备号(equipNum)进行分组,并获得某个设备号所对应的createTime值最大的那条记录
mysql
SELECT DISTINCT
code,
createTime,
equipNum,
reqTime,
wight
FROM
equip eq
JOIN (
SELECT
max( createTime ) mtime,
equipNum meq,
max( reqTime ) mreqt
FROM
equip eq
WHERE
createTime BETWEEN '1629424800000'
AND '1629425399999'
GROUP BY
eq.equipNum
) AS maxtime ON eq.equipNum = maxtime.meq
AND eq.createTime = maxtime.mtime
AND eq.reqt = maxtime.mreqt
ORDER BY
equipNum;
持久层
@Repository
public interface EquipDao extends JpaRepository<Equip,String>{
@Query(nativeQuery = true, value = "SELECT DISTINCT code,createTime,equipNum,reqTime,wight FROM equip eq JOIN ( SELECT max( createTime ) mtime, equipNum meq, max( reqTime ) mreqt FROM equip eq WHERE createTime BETWEEN ?1 AND ?2 GROUP BY eq.equipNum ) AS maxtime ON eq.equipNum = maxtime.meq AND eq.createTime = maxtime.mtime AND eq.reqt = maxtime.mreqt ORDER BY equipNum;")
public List<Equip> findAllByCreateTimeBetween(String startTime, String endTime);
}
问题:
运行后发现,输出的数据好多重复的,但是list.size()的大小和数据库中查的记录条数一致。
问题分析
其实问题是出在实体类中。@Id 这个注解虽然加了,但是我们去查看表结构的时候会发现,code 字段并没有成为主键,当然这也是我当前场景下所希望的结果,不要出现任何主键。然而,加了 @Id 注解的属性,却要临时的担起主键的部分职责,那就是唯一。但很明显,code 字段重复的记录很多,所以在最终的查询结果中,凡是 code 字段的值一样的记录,其他字段的值也都和第一条一样了。
解决
-
方案1:找个不会重复的字段就可以了。在不会重复的属性上边加上 @Id,如果需要用联合主键的,那么就给多个属性上边加 @Id
public class Equip{ @Id @Column(name = "code") private String code; @Id @Column(name = "createTime") private String createTime; @Id @Column(name = "equipNum") private String equipNum; @Column(name = "reqTime") private String reqTime; @Column(name = "wight") private String wight; }
-
方案2:然而我这个表实在特殊,无论哪个字段都会重复,甚至会出现两条一模一样的记录,单个主键和联合主键都行不通。
但是我这儿最终查询的结果,是基于 equipNum 分组的,group by equipNum,也就意味着最终的查询结果里的equipNum是唯一的,那就只需要给 equipNum 属性加上 @Id 注解即可public class Equip{ @Column(name = "code") private String code; @Column(name = "createTime") private String createTime; @Id //查询结果中equipNum不会重复,所以加在这里 @Column(name = "equipNum") private String equipNum; @Column(name = "reqTime") private String reqTime; @Column(name = "wight") private String wight; }