【问题标题】:How to show object's update history with Auditing?如何通过审核显示对象的更新历史记录?
【发布时间】:2018-06-24 09:53:11
【问题描述】:

我遇到了一个问题,我在 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>
{
}

【问题讨论】:

    标签: spring spring-boot


    【解决方案1】:

    您可以为此使用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 有效)时,输出应正确序列化。

    【讨论】:

    • 所以现在在我有我的 CRUD 的控制器中,我必须在上面 @Autowired 这个 NoteRevision 就可以了?我现在如何在 POSTMAN 中进行 HTTP 调用?我将在下面的帖子中向您展示我的控制器,您能告诉我我做得好吗?
    • 我在春天有点新,我不明白我现在如何在我的 Get 中使用这个功能,我必须在我的 NoteRepository 和 NoteController 中更改什么?我将它添加到我的主要帖子中。
    • 我的意思是,当我在 POSTMAN 中使用 GET 时,它会给我一个 JSON,例如:{title, content, created, updated},我怎样才能用那个函数得到它?我的意思是,当我发送此函数的 HTTP 调用时,我会以 JSON 格式获得所有版本的函数,例如 {title, content, created, updated}{title, content, created, updated}{title, content, created, updated}
    • 我的 http 调用给了我这样的答案:{ "com.example.mynotes.mynote.model.Note@c3877f8": "2018-06-24T13:57:28.325+0000" }, { "com.example.mynotes.mynote.model.Note@555b7e06": "2018-06-24T13:57:31.133+0000" }
    • 好的,谢谢。我会在全国比赛后检查它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 2023-03-19
    • 2018-05-29
    • 2012-03-04
    相关资源
    最近更新 更多