我try 仅在表具有审计字段时才 for each 插入或更新查询设置审计字段.经过大量的试验和错误,我得出了这个逻辑,使用VisitListener
:
public class AuditVisitListener implements VisitListener {
private static final Field<Timestamp> createdAt = field("created_at", Timestamp.class);
private static final Field<String> createdBy = field("created_by", String.class);
private static final Field<Timestamp> updatedAt = field("updated_at", Timestamp.class);
private static final Field<String> updatedBy = field("updated_by", String.class);
private Set<Field<?>> auditFields = new HashSet<>();
private UserContextHolder userContextHolder;
public AuditVisitListener(UserContextHolder userContextHolder) {
this.userContextHolder = userContextHolder;
}
@Override
public void visitStart(VisitContext context) {
QueryPart topLevelQuery = context.context().topLevel();
if(topLevelQuery instanceof SelectQuery<?>) {
// No need to process further since we don't set audit fields for select queries
return;
}
collectAuditFields(context.queryParts());
if(topLevelQuery instanceof InsertQuery<?> insertQuery) {
addAuditFieldsForInsert(insertQuery);
}
if(topLevelQuery instanceof UpdateQuery<?> updateQuery) {
addAuditFieldsForUpdate(updateQuery);
}
}
@Override
public void visitEnd(VisitContext context) {
// Without this line, audit fields are added
// even if there is no audit fields in a table.
auditFields.clear();
}
private void collectAuditFields(QueryPart[] queryParts) {
for(QueryPart queryPart : queryParts) {
if(queryPart instanceof Table<?> table) {
if(table.field(createdAt) != null){
auditFields.add(createdAt);
}
if(table.field(createdBy) != null) {
auditFields.add(createdBy);
}
if(table.field(updatedAt) != null) {
auditFields.add(updatedAt);
}
if(table.field(updatedBy) != null) {
auditFields.add(updatedBy);
}
}
}
}
private void addAuditFieldsForInsert(StoreQuery<?> storeQuery) {
if(auditFields.contains(createdAt)) {
storeQuery.addValue(createdAt, now());
}
if(auditFields.contains(createdBy)) {
storeQuery.addValue(createdBy, userId());
}
addAuditFieldsForUpdate(storeQuery);
}
private void addAuditFieldsForUpdate(StoreQuery<?> storeQuery) {
if(auditFields.contains(updatedAt)) {
storeQuery.addValue(updatedAt, now());
}
if(auditFields.contains(updatedBy)) {
storeQuery.addValue(updatedBy, userId());
}
}
private String userId() {
return userContextHolder.getUserId()
.orElseThrow(() -> new BusinessException(ErrorCode.EMPTY_USER_CONTEXT));
}
}
第一个问题是没有auditFields.clear()
,即使表中没有审计字段,也会添加审计字段.为什么会发生这种事呢?
第二个问题是,它适用于单行插入,但当INSERT语句有多行时,它只将审计字段设置为最后一行.例如,INSERT语句:
ctx.insertInto(BRAND, BRAND.NAME_EN, BRAND.NAME_KR, BRAND.STATUS)
.values("Heinz", "하인즈", (short)1)
.values("Hunts", "헌츠", (short)1)
.execute();
生成以下查询:
insert into `pms`.`brand` (`name_en`, `name_kr`, `status`, created_at, created_by, updated_at, updated_by)
values ('Heinz', '하인즈', 1, null, null, null, null),
('Hunts', '헌츠', 1, current_timestamp(), 'test@test.com', current_timestamp(), 'test@test.com')
但是,如果我插入多行,则Batch Insert再次起作用:
BrandRecord heinz = ctx.newRecord(BRAND);
heinz.setNameEn("Heinz");
heinz.setNameKr("하인즈");
heinz.setStatus((short)1);
BrandRecord hunts = ctx.newRecord(BRAND);
hunts.setNameEn("Hunts");
hunts.setNameKr("헌츠");
hunts.setStatus((short)1);
ctx.batchInsert(heinz, hunts).execute();
如何为多行INSERT或UPDATE语句设置审计字段?
作为参考,AuditVisitListener
使用VisitListenerProvider
进行配置:
@Configuration
public class JooqConfig {
@Bean
public DefaultConfigurationCustomizer customizer(UserContextHolder userContextHolder) {
return configuration -> {
configuration.setVisitListenerProvider(() -> new AuditVisitListener(userContextHolder));
};
}
}
jOOQ: 3.18
Java: 17
Spring Boot: 3.0.5