我目前正在做一个有多个程序的项目,并且程序有多个视频,所以我像这样设置了我的 Program 和 ProgramVideo 实体。
@Entity
@SuperBuilder
@Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "program")
public class Program extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "title")
private String title;
//other fields I'm using
@OneToMany(mappedBy = "program", cascade = CascadeType.ALL)
private List<ProgramVideo> programVideo = new ArrayList<>();
@Entity
@SuperBuilder
@Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "program_video")
public class ProgramVideo extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "title")
private String title;
//other fields I'm using
@OneToMany(mappedBy = "programVideo", cascade = CascadeType.ALL)
@JsonIgnore
private List<VideoRecordHistory> videoRecordHistoryList = new ArrayList<>();
这里我没有在 ProgramVideo 实体中添加 @ManyToOne 关系,因为我认为我不需要从 ProgramVideo 端获取 Program 信息。
设置了 ProgramVideo 和 Program 实体后,我想实现一种方法来跟踪用户是否开始观看视频以及他们观看视频的时间点。
为了实现这个功能,我决定为
制作API在 VideoRecordHistory 表中创建一个条目以指示用户是否开始/单击观看特定视频。
不时更新创建的条目,其中包含视频正在播放的时间点的信息。
VideoRecordHistory 实体/表如下所示。
@Entity
@SuperBuilder
@Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "video_record_history")
public class VideoRecordHistory extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "program_video_id")
@JsonIgnore
private ProgramVideo programVideo;
@Column(name = "member_id")
private Long memberId;
@Column(name = "video_length")
private String videoLength;
@Column(name = "watched_to")
private String watchedTo;
}
使用第一个 API,创建一个包含 programVideoId、memberId 和 videoLength 的条目。
要记住的一件事是,我没有在该表和我的成员表之间添加任何关系,因为它会创建循环关系,因为它可能会导致堆栈溢出。
因此,用户注册到一个程序并单击该程序以查看program_videos 列表以及每个视频的进度(来自VideoRecordHistory 表)。
ProgramVideo <-> VideoRecordHistory 之间的关系本质上是 OneToMany,因为我不会为每个用户的视频历史记录创建一个表。我想在一张表中使用programVideoId 和memberId 跟踪所有记录。 由于它是 OneToMany 并设置如下,
@OneToMany(mappedBy = "programVideo", cascade = CascadeType.ALL)
@JsonIgnore
private List<VideoRecordHistory> videoRecordHistoryList = new ArrayList<>();
当我使用以下查询时
public List<ProgramVideo> getProgramVideoListAndProgressByProgramAndMember(Long programId, Long memberId) {
return queryFactory
.select(programVideo)
.from(programVideo)
.leftJoin(videoRecordHistory)
.on(programVideo.eq(videoRecordHistory.programVideo))
.where(
programVideo.program.id.eq(programId),
videoRecordHistory.memberId.eq(memberId)
)
.fetch();
}
它总是需要一个 videoRecordHistory 列表 AND 为我提供所有用户的记录,而无需使用此行过滤掉特定成员
videoRecordHistory.memberId.eq(memberId)
我尝试过的另一个查询是
public List<ProgramVideo> getProgramVideoListAndProgressByProgramAndMember(Long programId, Long memberId) {
return queryFactory
.select(programVideo)
.from(programVideo)
.leftJoin(videoRecordHistory)
.on(programVideo.eq(videoRecordHistory.programVideo)
.and(videoRecordHistory.memberId.eq(memberId))
)
.where(
programVideo.program.id.eq(programId)
)
.fetch();
}
在“ON”子句中添加 videoRecordHistory 成员限制。
这给了我与之前查询所有成员的视频历史记录相同的结果......
我想使用 ProgramId 获取 ProgramVideo 列表,并使用 memberId 获取特定用户的进度。
或
我认为将 VideoRecordHistory 作为 Programvideo 实体中的列表会导致问题,迫使结果始终是一个列表,而且,即使有 memberId 限制,我仍然可以获得所有成员的记录。
原始查询:
2023-12-20T14:25:50.671+09:00 DEBUG 99522 --- [nio-8080-exec-2] org.hibernate.SQL :
select
p1_0.id,
p1_0.created_at,
p1_0.created_id,
p1_0.day_count,
p1_0.has_assignment,
p1_0.media_url,
p1_0.program_id,
p1_0.title,
p1_0.updated_at,
p1_0.updated_id,
p1_0.video_point,
p1_0.video_summary,
p1_0.video_thumbnail
from
program_video p1_0
left join
video_record_history v1_0
on p1_0.id=v1_0.program_video_id
and v1_0.member_id=?
where
p1_0.program_id=?
Hibernate:
select
p1_0.id,
p1_0.created_at,
p1_0.created_id,
p1_0.day_count,
p1_0.has_assignment,
p1_0.media_url,
p1_0.program_id,
p1_0.title,
p1_0.updated_at,
p1_0.updated_id,
p1_0.video_point,
p1_0.video_summary,
p1_0.video_thumbnail
from
program_video p1_0
left join
video_record_history v1_0
on p1_0.id=v1_0.program_video_id
and v1_0.member_id=?
where
p1_0.program_id=?
查看Hibernate SQL,我意识到它只是查询属于program_video 的列。 所以我使用
Projection.fields
方法来自定义输出的每一列
所以我做了一个像下面这样的DTO
@Getter
@SuperBuilder
@NoArgsConstructor
public class ProgramVideoAndProgressResponseDto {
private String title;
private String videoThumbnail;
private String videoLength;
private String watchedTo;
private CompletionStatus completionStatus;
并将其实现到我的 querydsl 查询中:
return queryFactory
.select(Projections.fields(ProgramVideoAndProgressResponseDto.class,
programVideo.title,
programVideo.videoThumbnail,
videoRecordHistory.videoLength,
videoRecordHistory.watchedTo
))
.from(programVideo)
.leftJoin(videoRecordHistory)
.on(programVideo.eq(videoRecordHistory.programVideo)
.and(videoRecordHistory.memberId.eq(memberId))
)
.where(
programVideo.program.id.eq(programId)
)
.fetch();
}