Spring 持久分离实体时数据 JPA 审计失败
Spring Data JPA auditing fails when persisting detached entity
我已经使用 Spring Data JPA AuditingEntityListener 和 AuditorAware bean 设置了 JPA 审计。我想要的是即使在具有预定义标识符的实体上也能够保留审计员详细信息。
问题是,当具有预定义 ID 的 JPA 实体被持久化和刷新时,它的审计员详细信息无法被持久化:
object references an unsaved transient instance - save the transient instance before flushing: me.auditing.dao.AuditorDetails
有趣的是,当保存具有生成的 ID 的实体时 - 一切都很好。在这两种情况下,实体都是新的。我无法查明通过休眠代码挖掘的问题,所以我创建了一个 sample project 来证明这一点(测试 class me.auditing.dao.AuditedEntityIntegrationTest)它同时具有具有预定义和生成的标识符的实体,应该被审计。
实体是:
@Entity
public class AuditedEntityWithPredefinedId extends AuditableEntity {
@Id
private String id;
public String getId() {
return id;
}
public AuditedEntityWithPredefinedId setId(String id) {
this.id = id;
return this;
}
}
和:
@Entity
public class AuditedEntityWithGeneratedId extends AuditableEntity {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
public String getId() {
return id;
}
public AuditedEntityWithGeneratedId setId(String id) {
this.id = id;
return this;
}
}
其中父 class 是:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity implements Serializable {
private static final long serialVersionUID = -7541732975935355789L;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@CreatedBy
private AuditorDetails createdBy;
@CreatedDate
private LocalDateTime createdDate;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@LastModifiedBy
private AuditorDetails modifiedBy;
@LastModifiedDate
private LocalDateTime modifiedDate;
审计员 getter 实施是:
@Override
public AuditorDetails getCurrentAuditor() {
return new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
}
Edit 2016-08-08: 貌似保存一个新的预定义id的entity时,会得到两个不同的createdBy和modifiedBy AuditorDetails实例,挺符合逻辑的如果该实体实际上不是新的。因此,一个具有 generated 的全新实体会同时获取同一实例的 AuditorDetails,而手动设置 id 的实体则不会。我通过在 AuditorAware bean 中保存审核员详细信息,然后将其返回给 AuditingHandler 来对其进行测试。
好的,所以现在我能找到的唯一解决方案是在将 AuditorDetails 写入被审计的实体之前实际保留它,如下所示:
@Override
@Transactional
public AuditorDetails getCurrentAuditor() {
AuditorDetails details = new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
return auditorDetailsRepository.save(details);
}
这不是最优雅的解决方案,但目前有效。
我已经使用 Spring Data JPA AuditingEntityListener 和 AuditorAware bean 设置了 JPA 审计。我想要的是即使在具有预定义标识符的实体上也能够保留审计员详细信息。 问题是,当具有预定义 ID 的 JPA 实体被持久化和刷新时,它的审计员详细信息无法被持久化:
object references an unsaved transient instance - save the transient instance before flushing: me.auditing.dao.AuditorDetails
有趣的是,当保存具有生成的 ID 的实体时 - 一切都很好。在这两种情况下,实体都是新的。我无法查明通过休眠代码挖掘的问题,所以我创建了一个 sample project 来证明这一点(测试 class me.auditing.dao.AuditedEntityIntegrationTest)它同时具有具有预定义和生成的标识符的实体,应该被审计。
实体是:
@Entity
public class AuditedEntityWithPredefinedId extends AuditableEntity {
@Id
private String id;
public String getId() {
return id;
}
public AuditedEntityWithPredefinedId setId(String id) {
this.id = id;
return this;
}
}
和:
@Entity
public class AuditedEntityWithGeneratedId extends AuditableEntity {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
public String getId() {
return id;
}
public AuditedEntityWithGeneratedId setId(String id) {
this.id = id;
return this;
}
}
其中父 class 是:
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity implements Serializable {
private static final long serialVersionUID = -7541732975935355789L;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@CreatedBy
private AuditorDetails createdBy;
@CreatedDate
private LocalDateTime createdDate;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@LastModifiedBy
private AuditorDetails modifiedBy;
@LastModifiedDate
private LocalDateTime modifiedDate;
审计员 getter 实施是:
@Override
public AuditorDetails getCurrentAuditor() {
return new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
}
Edit 2016-08-08: 貌似保存一个新的预定义id的entity时,会得到两个不同的createdBy和modifiedBy AuditorDetails实例,挺符合逻辑的如果该实体实际上不是新的。因此,一个具有 generated 的全新实体会同时获取同一实例的 AuditorDetails,而手动设置 id 的实体则不会。我通过在 AuditorAware bean 中保存审核员详细信息,然后将其返回给 AuditingHandler 来对其进行测试。
好的,所以现在我能找到的唯一解决方案是在将 AuditorDetails 写入被审计的实体之前实际保留它,如下所示:
@Override
@Transactional
public AuditorDetails getCurrentAuditor() {
AuditorDetails details = new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
return auditorDetailsRepository.save(details);
}
这不是最优雅的解决方案,但目前有效。