文章目录
概要
基于Springboot框架实现的审核机制。实现用户修改属性的审核机制和数据库访问冲突的解决
整体架构流程
流程解释
- 当一个属性被创建或修改时,会在属性表中插入或更新一条记录,并将状态设置为
PENDING
(待审核)。 - 同时,在审核表中插入一条新的审核记录,记录下申请人ID、操作类型、属性ID等信息,状态同样设置为
PENDING
。 - 管理员会根据审核表中的记录进行审核,更新审核记录的状态为
APPROVED
或REJECTED
,并记录审核人ID。 - 如果审核通过,属性表中的对应属性记录的状态也会更新为
APPROVED
;如果审核拒绝,属性表中的状态更新为REJECTED
。审核表和属性表的updated_at
字段会在每次记录更新时自动更新为当前时间。 - 前端请求得到审核通过的属性。
- 数据库返回所有审核通过的属性。
这个过程确保了属性的变化都经过审核流程,并且所有的审核操作都有记录可查。
流程详细介绍
数据库设计
/*
属性表 (Attributes):
id (主键)
name (属性名称)
value (属性值)
status (审核状态: 待审核、审核通过、审核拒绝)
created_at (创建时间)
updated_at (更新时间)
审核表 (AuditLogs):
*/
/*
审核表:
id (主键)
attribute_id (外键,指向属性表)
action (操作: 增加、修改、删除)
requested_by (申请人ID)
admin_reviewed (审核人ID)
status (审核结果: 待审核、通过、拒绝)
created_at (创建时间)
updated_at (更新时间)
*/
public enum Status {
PENDING, // 待审核
APPROVED, // 已通过
REJECTED // 已拒绝
}
数据模型解释
属性表 (Attributes)
这个表用于存储属性信息:
id
:主键,用于唯一标识一个属性记录。name
:属性名称,表示属性的名称。value
:属性值,表示属性的值。status
:审核状态,用于标识属性当前所处的审核阶段,可能的值有“待审核”、“审核通过”和“审核拒绝”。created_at
:创建时间,记录属性记录创建的时间。updated_at
:更新时间,记录属性记录最后更新的时间。
审核表 (AuditLogs)
这个表用于记录属性的审核日志:
id
:主键,用于唯一标识一条审核记录。attribute_id
:外键,指向属性表的主键,表示这条审核记录对应的属性。action
:操作,表示申请人进行的操作类型,可能的值有“增加”、“修改”和“删除”。requested_by
:申请人ID,表示发起审核请求的用户标识。admin_reviewed
:审核人ID,表示进行审核的管理员标识。status
:审核结果,表示审核的最终结果,可能的值有“待审核”、“通过”和“拒绝”。created_at
:创建时间,记录审核记录创建的时间。updated_at
:更新时间,记录审核记录最后更新的时间。
枚举类解释
Status 枚举
这个枚举类用于定义审核状态,包含以下三个枚举值:
PENDING
:表示“待审核”状态,属性或审核记录正在等待审核。APPROVED
:表示“已通过”状态,属性或审核记录已经通过审核。REJECTED
:表示“已拒绝”状态,属性或审核记录未通过审核。
数据库加锁
数据库加锁从冲突角度常分为乐观锁和悲观锁
悲观锁(Pessimistic Locking)
- 锁定资源:悲观锁在操作数据之前会锁定资源,直到事务完成才会释放锁。
- 阻塞:如果资源被锁定,其他尝试访问该资源的操作将被阻塞,直到锁被释放。
- 适用场景:适用于写入操作多、读取操作少,或者对数据一致性要求极高的场景。
乐观锁(Optimistic Locking)
- 无锁操作:乐观锁在读取数据时不锁定资源,而是在更新时检查数据是否被修改过。
- 无阻塞:不会阻塞其他事务,因此可以提供更高的并发性能。
- 适用场景:适用于读取操作多、写入操作少。
读取大于写入操作,且每一条操作都会在审核表中记录,不会存在对资源的冲突返回,所以聚焦点在于确保自上次读取数据以来没有其他事务修改过数据。所以我们选用乐观锁来实现。
乐观锁实现步骤
使用JPA+Hibernate实现
- 字段添加:在属性表中添加一个
version
字段,该字段在每次实体被更新时递增。 - 注解使用:在实体类的
version
字段上使用@Version
注解,这样Hibernate(或其他JPA实现)就知道这个字段是用来进行乐观锁控制的。 - 更新操作:当实体被更新时,Hibernate会自动检查
version
字段。在更新之前,它会读取当前实体的version
值,并在更新时将其与数据库中的version
值进行比较。 - 冲突检测:如果在读取和更新之间
version
值在数据库中被其他事务修改,则更新操作将失败,因为当前事务中的version
值与数据库中的值不匹配。
以下是属性表实体类中version
字段的示例代码:
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
import java.util.Date;
@Entity
public class Attribute {
@Id
private Long id; // 主键
private String name; // 属性名称
private String value; // 属性值
private String status; // 审核状态: 待审核、审核通过、审核拒绝
private Date created_at; // 创建时间
private Date updated_at; // 更新时间
@Version
private Integer version; // 乐观锁版本号
// 省略构造函数、getter和setter方法
}
每次Attribute
实体被更新时,version
字段都会自动递增。如果两个事务尝试同时更新同一个实体,只有第一个提交的事务会成功,第二个事务会因为版本号不匹配而失败,通常会抛出一个乐观锁异常(例如OptimisticLockException
)。
这种方法确保了即使在高度并发的环境中,也能保持数据的一致性,并且避免了不必要的锁定开销。
小结
分享博主在开发中遇到的问题,如果你也感兴趣,关注点赞博主,欢迎交流讨论