如何使用审核显示对象的更新历史记录?

时间:2018-06-24 09:53:11

标签: spring spring-boot

我遇到了一个问题,我使用MYSQL在springboot中创建了一个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>
{
}

1 个答案:

答案 0 :(得分:0)

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