spring - 如何通过审核显示对象的更新历史记录?
问题描述
我遇到了一个问题,我在 springboot 中用 MYSQL 做了一个 CRUD,现在我想创建一个方法来返回我的对象的更新历史......
我有这样的课:
@Entity
@Table
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt", "updatedAt"}, allowGetters = true)
@Audited
public class Note implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Getter
@Setter
private Long id;
@NotBlank
@Getter
@Setter
private String title;
@Version
@Getter
@Setter
private long version;
@NotBlank
@Getter
@Setter
private String content;
@Column(nullable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
@Getter
@Setter
private Date createdAt;
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
@Getter
@Setter
private Date updatedAt;
}
但我不知道现在如何创建一个 HTTP 调用来显示 @Audited 的更新历史记录。
我发现了这样的东西:Find max revision of each entity less than or equal to given revision with envers
但我不知道如何在我的项目中实现它......
@RestController
@RequestMapping("/api")
public class NoteController
{
@Autowired
NoteRevisionService noteRevisionService;
@Autowired
NoteRepository noteRepository;
// Get All Notes
@GetMapping("/notes")
public List<Note> getAllNotes() {
return noteRepository.findAll();
}
// Create a new Note
@PostMapping("/notes")
public Note createNote(@Valid @RequestBody Note note) {
return noteRepository.save(note);
}
// Get a Single Note
@GetMapping("/notes/{id}")
public Note getNoteById(@PathVariable(value = "id") Long noteId) {
return noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
}
@GetMapping("/notes/{id}/version")
public List<?> getVersions(@PathVariable(value = "id") Long noteId)
{
return noteRevisionService.getNoteUpdates(noteId);
}
// Update a Note
@PutMapping("/notes/{id}")
public Note updateNote(@PathVariable(value = "id") Long noteId,
@Valid @RequestBody Note noteDetails) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
note.setTitle(noteDetails.getTitle());
note.setContent(noteDetails.getContent());
Note updatedNote = noteRepository.save(note);
return updatedNote;
}
// Delete a Note
@DeleteMapping("/notes/{id}")
public ResponseEntity<?> deleteNote(@PathVariable(value = "id") Long noteId) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
noteRepository.delete(note);
return ResponseEntity.ok().build();
}
}
getVersions 是 Joe Doe 发给我的函数调用。
那里:存储库
@Repository
public interface NoteRepository extends JpaRepository<Note, Long>
{
}
解决方案
你可以用AuditQuery
这个。下面的getNoteUpdates
方法返回一个映射列表。每个映射都包含一个对象状态和导致该状态的更新时间。
@Service
@Transactional
public class NoteRevisionService {
private static final Logger logger = LoggerFactory.getLogger(NoteRevisionService.class);
@PersistenceContext
private EntityManager entityManager;
@SuppressWarnings("unchecked")
public List<Map.Entry<Note, Date>> getNoteUpdates(Long noteId) {
AuditReader auditReader = AuditReaderFactory.get(entityManager);
AuditQuery query = auditReader.createQuery()
.forRevisionsOfEntity(Note.class, false, false)
.add(AuditEntity.id().eq(noteId)) // if you remove this line, you'll get an update history of all Notes
.add(AuditEntity.revisionType().eq(RevisionType.MOD)); // we're only interested in MODifications
List<Object[]> revisions = (List<Object[]>) query.getResultList();
List<Map.Entry<Note, Date>> results = new ArrayList<>();
for (Object[] result : revisions) {
Note note = (Note) result[0];
DefaultRevisionEntity revisionEntity = (DefaultRevisionEntity) result[1];
logger.info("The content of the note updated at {} was {}", revisionEntity.getRevisionDate(), note.getContent());
results.add(new SimpleEntry<>(note, revisionEntity.getRevisionDate()));
}
return results;
}
}
请注意,如果您可以以某种方式限制查询(例如通过过滤属性),则绝对应该这样做,因为否则执行查询会对整个应用程序的性能产生负面影响(返回列表的大小可能如果这个对象经常被更新,那就是巨大的)。
由于该类已使用注释进行@Service
注释,因此您可以像任何其他常规 Spring bean 一样注入/自动装配NoteRevisionService
,特别是在处理 GET 请求并委托给该服务的控制器中。
更新
我不知道必须采取额外的步骤来序列化地图条目列表。可能有更好的解决方案,但以下方法可以完成工作,您可以revisionDate
使用简单的注释自定义输出格式。
你需要定义另一个类,比如说NoteUpdatePair
,像这样:
public class NoteUpdatePair {
private Note note;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date revisionDate; // this field is of type java.util.Date (not java.sql.Date)
NoteUpdatePair() {}
public NoteUpdatePair(Note note, Date revisionDate) {
this.note = note;
this.revisionDate = revisionDate;
}
public Note getNote() {
return note;
}
public void setNote(Note note) {
this.note = note;
}
public Date getRevisionDate() {
return revisionDate;
}
public void setRevisionDate(Date revisionDate) {
this.revisionDate = revisionDate;
}
}
现在,您将返回NodeUpdatePair
对象列表,而不是返回映射条目列表:
@Service
@Transactional
public class NoteRevisionService {
private static final Logger logger = LoggerFactory.getLogger(NoteRevisionService.class);
@PersistenceContext
private EntityManager entityManager;
@SuppressWarnings("unchecked")
public List<NoteUpdatePair> getNoteUpdates(Long noteId) {
AuditReader auditReader = AuditReaderFactory.get(entityManager);
AuditQuery query = auditReader.createQuery()
.forRevisionsOfEntity(Note.class, false, false)
.add(AuditEntity.id().eq(noteId)) // if you remove this line, you'll get an update history of all Notes
.add(AuditEntity.revisionType().eq(RevisionType.MOD)); // we're only interested in MODifications
List<Object[]> revisions = (List<Object[]>) query.getResultList();
List<NoteUpdatePair> results = new ArrayList<>();
for (Object[] result : revisions) {
Note note = (Note) result[0];
DefaultRevisionEntity revisionEntity = (DefaultRevisionEntity) result[1];
logger.info("The content was {}, updated at {}", note.getContent(), revisionEntity.getRevisionDate());
results.add(new NoteUpdatePair(note, revisionEntity.getRevisionDate()));
}
return results;
}
}
关于你关于服务使用的问题,我可以看到你已经将它自动连接到你的控制器中,所以你需要做的就是在你的NoteController
:
@RestController
@RequestMapping("/api")
public class NoteController {
@Autowired
private NoteRevisionService revisionService;
/*
the rest of your code...
*/
@GetMapping("/notes/{noteId}/updates")
public List<NoteUpdatePair> getNoteUpdates(@PathVariable Long noteId) {
return revisionService.getNoteUpdates(noteId);
}
}
~/api/notes/1/updates
现在,当您向(假设有效)发送 GET 请求时nodeId
,输出应该被正确序列化。
推荐阅读
- reactjs - 使用 HTTPS 而不是 HTTP 时服务器超时?
- redhat - 如何将 Mutiny String 变量数据设置为类变量?
- python - 使用 Matplotlib 仅绘制月份的时间序列图
- c++ - 为什么我的矩阵求逆在 C++ 中使用 LAPACKE 这么慢:MAGMA Alternative and setup
- python - 使用 for 和 if 在同一行生成过滤列表
- api - context.Variable.GetValueOrDefault
("var1") 不在 xslt 策略中访问 - python - Python - 使用 API 更新 dict 中的值时出错
- mysql - MySQL更新或插入取决于NULL?
- android - 如何单击堆栈颤动中位于主屏幕后面的屏幕上的按钮
- laravel - laravel jetstrap 问题 - 页面不成形