jpa - Spring JpaRepository 保存导致生成 100 次选择
问题描述
当我在 JpaRepository 上调用 save 时,我遇到了非常奇怪的错误。该代码似乎微不足道,所以我只能认为它与配置有关。
基本上我在存储库上调用了一个 findBy 方法,这完全按照预期返回了一个 dao 对象列表,我通过这个列表设置一个时间戳,然后在每个 dao 上调用 save(),一旦第一次保存被调用,日志就被填满使用数百个相同的选择语句,在我只是试图保存的 dao 上进行选择,直到最终出现堆栈溢出。这可能是什么原因???(注意最初我在 dao 对象列表上调用了 saveAll() - 这个,不出所料有同样的错误)
public void triggerDeletion() {
ZonedDateTime expiryDate = ZonedDateTime.now().minus(daysAfterExpiry);
// Get list of conversations to delete
List<ConversationDao> conversationDaoList = conversationDatabaseService.retrieveExpiredConversations(expiryDate);
// Mark each conversation as deleted by setting its deletion timestamp to now.
conversationDaoList.stream().forEach(conversation->delete(conversation));
}
public void delete(ConversationDao conversationDao){
conversationDao.setDeletionTimestamp(new Timestamp(System.currentTimeMillis()));
conversationDatabaseService.save( conversationDao ); <------ This generates 100s of select statements.
}
...
// ConversationDatabaseService Interface
////////////////////////////////////////
public interface ConversationDatabaseService {
void save(ConversationDao conversationDao);
}
...
// Conversation DatabaseService Class
/////////////////////////////////////
@Service
public class ConversationDatabaseServiceImpl implements ConversationDatabaseService {
private final ConversationRepository conversationRepository;
public void save(ConversationDao conversationDao) {
conversationRepository.saveAndFlush(conversationDao); // Just save has also been tried.
}
}
...
// ConversationRepository interface
///////////////////////////////////
public interface ConversationRepository extends JpaRepository<ConversationDao, String> {
List<ConversationDao> findByExpiryDateLessThanAndDeletionTimestampIsNull(Timestamp expiryDate);
}
...
// ConversationDao
//////////////////////////////////
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.UpdateTimestamp;
@Entity
@Table(name = "DOCX_CONVERSATION")
public class ConversationDao {
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2",
strategy = "uuid2")
@Column(name = "CONVERSATION_ID",
columnDefinition = "char")
private String conversationId;
@Column(name = "PROCESS_CODE")
private String processCode;
@Column(name = "PROFILE_ID",
columnDefinition = "char")
private String profileId;
@Column(name = "REMINDER_EMAIL_SENT",
columnDefinition = "char")
private String reminderEmailSent;
@Column(name = "EXPIRY_DATE")
private Date expiryDate;
@Column(name = "DELETION_TIMESTAMP")
private Timestamp deletionTimestamp;
@Column(name = "LAST_LOGIN_TIMESTAMP")
private Timestamp lastLoginTimestamp;
@Column(name = "CREATED_USER_ID",
columnDefinition = "char")
private String colleagueId;
@Column(name = "CREATED_TIMESTAMP",
updatable = false)
@CreationTimestamp
private Timestamp createdTimestamp;
@Column(name = "LAST_UPDATED_USER_ID",
columnDefinition = "char")
private String lastUpdatedUserId;
@Column(name = "LAST_UPDATED_TIMESTAMP")
@UpdateTimestamp
private Timestamp lastUpdatedTimestamp;
@OneToMany(mappedBy = "conversation",
cascade = CascadeType.ALL)
private Set<MessageDetailDao> messageDetailDaoSet = new HashSet<>();
@OneToMany(mappedBy = "fileDetailId.conversation",
cascade = CascadeType.ALL)
private Set<FileDetailDao> fileDetailDaoSet = new HashSet<>();
@OneToMany(mappedBy = "conversation",
cascade = CascadeType.ALL)
private Set<ParameterDao> parameterDaoSet = new HashSet<>();
@OneToMany(mappedBy = "conversation",
cascade = CascadeType.ALL)
private Set<NotificationDao> notificationDaoSet = new HashSet<>();
public ConversationDao() {
}
public ConversationDao(String processCode, Date expiryDate,
String colleagueId, String lastUpdatedUserId) {
this.processCode = processCode;
this.reminderEmailSent = "N";
this.expiryDate = expiryDate;
this.colleagueId = colleagueId;
this.lastUpdatedUserId = lastUpdatedUserId;
}
public String getConversationId() {
return conversationId;
}
public void setConversationId(String conversationId) {
this.conversationId = conversationId;
}
public String getProcessCode() {
return processCode;
}
public void setProcessCode(String processCode) {
this.processCode = processCode;
}
public String getProfileId() {
return profileId;
}
public void setProfileId(String profileId) {
this.profileId = profileId;
}
public String getReminderEmailSent() {
return reminderEmailSent;
}
public void setReminderEmailSent(String reminderEmailSent) {
this.reminderEmailSent = reminderEmailSent;
}
public Date getExpiryDate() {
return expiryDate;
}
public void setExpiryDate(Date expiryDate) {
this.expiryDate = expiryDate;
}
public Timestamp getDeletionTimestamp() {
return deletionTimestamp;
}
public void setDeletionTimestamp(Timestamp deletionTimestamp) {
this.deletionTimestamp = deletionTimestamp;
}
public Timestamp getLastLoginTimestamp() {
return lastLoginTimestamp;
}
public void setLastLoginTimestamp(Timestamp lastLoginTimestamp) {
this.lastLoginTimestamp = lastLoginTimestamp;
}
public String getColleagueId() {
return colleagueId.trim();
}
public void setColleagueId(String colleagueId) {
this.colleagueId = colleagueId;
}
public Timestamp getCreatedTimestamp() {
return createdTimestamp;
}
public void setCreatedTimestamp(Timestamp createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
public String getLastUpdatedUserId() {
return lastUpdatedUserId;
}
public void setLastUpdatedUserId(String lastUpdatedUserId) {
this.lastUpdatedUserId = lastUpdatedUserId;
}
public Timestamp getLastUpdatedTimestamp() {
return lastUpdatedTimestamp;
}
public void setLastUpdatedTimestamp(Timestamp lastUpdatedTimestamp) {
this.lastUpdatedTimestamp = lastUpdatedTimestamp;
}
public void addMessageDetailDao(MessageDetailDao messageDetailDao) {
messageDetailDaoSet.add(messageDetailDao);
messageDetailDao.setConversation(this);
}
public Set<MessageDetailDao> getMessageDetailDao() {
return messageDetailDaoSet;
}
public void addFileDetailDao(FileDetailDao fileDetailDao) {
fileDetailDaoSet.add(fileDetailDao);
fileDetailDao.getId().setConversation(this);
}
public Set<FileDetailDao> getFileDetailDao() {
return fileDetailDaoSet;
}
public void addParameterDao(ParameterDao parameterDao) {
parameterDaoSet.add(parameterDao);
parameterDao.setConversation(this);
}
public Set<ParameterDao> getParameterDao() {
return parameterDaoSet;
}
public void addNotificationDao(NotificationDao notificationDao) {
notificationDaoSet.add(notificationDao);
notificationDao.setConversation(this);
}
public Set<NotificationDao> getNotificationDao() {
return notificationDaoSet;
}
}
}
这会在破坏堆栈之前生成这些相同的选择语句 100 次:-
1TS="20190411.075004.028" 4MSG="binding parameter [1] as [VARCHAR] - [6df49d6f-216b-460d-9282-2d25c098f9fe]" 1EVC="TRACE" 2CLS="BasicBinder"
1TS="20190411.075004.065" 4MSG="extracted value ([convers16_10_0_] : [VARCHAR]) - [6df49d6f-216b-460d-9282-2d25c098f9fe]" 1EVC="TRACE" 2CLS="BasicExtractor"
1TS="20190411.075004.069" 4MSG="extracted value ([file_met1_10_0_] : [VARCHAR]) - [4E90FD20-FC59-41B8-BF8E-88B7D682A87C]" 1EVC="TRACE" 2CLS="BasicExtractor"
1TS="20190411.075004.074" 4MSG="select conversati0_.conversation_id as conversa1_5_1_, conversati0_.created_user_id as created_2_5_1_, conversati0_.created_timestamp as created_3_5_1_, conversati0_.deletion_timestamp as deletion4_5_1_, conversati0_.expiry_date as expiry_d5_5_1_, conversati0_.last_login_timestamp as last_log6_5_1_, conversati0_.last_updated_timestamp as last_upd7_5_1_, conversati0_.last_updated_user_id as last_upd8_5_1_, conversati0_.process_code as process_9_5_1_, conversati0_.profile_id as profile10_5_1_, conversati0_.reminder_email_sent as reminde11_5_1_, filedetail1_.conversation_id as convers16_10_3_, filedetail1_.file_metadata_id as file_met1_10_3_, filedetail1_.conversation_id as convers16_10_0_, filedetail1_.file_metadata_id as file_met1_10_0_, filedetail1_.application_id as applicat2_10_0_, filedetail1_.case_id as case_id3_10_0_, filedetail1_.created_user_id as created_4_10_0_, filedetail1_.content_type as content_5_10_0_, filedetail1_.created_by as created_6_10_0_, filedetail1_.created_timestamp as created_7_10_0_, filedetail1_.document_code as document8_10_0_, filedetail1_.document_owner as document9_10_0_, filedetail1_.document_repository_state as documen10_10_0_, filedetail1_.file_name as file_na11_10_0_, filedetail1_.file_size_bytes as file_si12_10_0_, filedetail1_.file_state as file_st13_10_0_, filedetail1_.file_version as file_ve14_10_0_, filedetail1_.updated_by as updated15_10_0_ from docx_conversation conversati0_ left outer join docx_file_detail filedetail1_ on conversati0_.conversation_id=filedetail1_.conversation_id where conversati0_.conversation_id=?" 1EVC="DEBUG" 2CLS="SQL"
到底是怎么回事 ??
更多信息,原因肯定与道的这一部分有关:-
@OneToMany(mappedBy = "fileDetailId.conversation",cascade = CascadeType.ALL)
private Set<FileDetailDao> fileDetailDaoSet = new HashSet<>();
FileDetailDao 包含对 FileDetailId 的嵌入式引用
@EmbeddedId
private FileDetailId fileDetailId;
这个 FileDetailId 包含对 join 的 conversationId 的引用:-
@Embeddable
public class FileDetailId implements Serializable {
@Column(name = "FILE_METADATA_ID", columnDefinition = "char")
private String fileMetadataId;
@ManyToOne
@JoinColumn(name = "CONVERSATION_ID")
private ConversationDao conversation;
//etc
}
解决方案
我不知道为什么,但是如果我将级联类型更改为 REMOVE 它可以正常工作....
@OneToMany(mappedBy = "fileDetailId.conversation",cascade = CascadeType.REMOVE)
private Set<FileDetailDao> fileDetailDaoSet = new HashSet<>();
推荐阅读
- c - 为什么 netfilter.h 中缺少 nf_hookfn、nf_hook_ops 等的 typedef?
- javascript - 连接客户端和服务器端打字稿项目
- c# - 检查字节数组是否包含另一个字节数组C#的性能方法
- javascript - 如何从 catch 块中的 fetch 获取错误响应正文?
- javascript - JavaScript try catch 代码片段的含义?
- javascript - 引导程序 4 中的完整内容 iframe
- gradle - 如何判断 gradle 插件属性的评估何时会被推迟?
- laravel - 将动态值传递给 Laravel 最大验证规则
- sql - BigQuery Firebase 游戏中每个级别的平均硬币
- reactjs - Antd Table React中分别处理分页排序和过滤