You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Data JPA @ManyToMany关联时PostgreSQL created_at非空约束异常解决

解决Spring Data JPA中多对多关联表审计字段的非空约束异常

问题根源

你同时使用了@ManyToMany的自动关联表配置和独立的RolePermission实体映射同一张role_permissions表,导致Hibernate维护多对多关系时,只会插入role_id和permission_id两个字段,无法填充created_at、granted_at等审计字段,触发数据库非空约束错误。

解决方案

放弃@ManyToMany的自动关联逻辑,改用双向一对多+多对一的关联方式,通过RolePermission实体完全管理关联关系,以此实现对所有审计字段的手动控制。


1. 修改Role实体

移除原有的@ManyToMany和@JoinTable配置,替换为与RolePermission的一对多关联:

@Entity
public class Role extends BaseEntity {
    @OneToMany(mappedBy = "role", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonProperty("permissions")
    private Set<RolePermission> rolePermissions = new HashSet<>();

    // 新增权限时创建RolePermission实例并填充审计字段
    public void addPermission(Permission permission, String grantedBy) {
        RolePermission rolePermission = RolePermission.builder()
                .role(this)
                .permission(permission)
                .grantedAt(Instant.now())
                .grantedBy(grantedBy)
                .build();
        this.rolePermissions.add(rolePermission);
        permission.getRolePermissions().add(rolePermission);
    }

    // 保留原有getter/setter
}

2. 修改Permission实体

同样替换为与RolePermission的一对多关联:

@Entity
public class Permission extends BaseEntity {
    @OneToMany(mappedBy = "permission", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<RolePermission> rolePermissions = new HashSet<>();

    // 保留getter/setter
    public Set<RolePermission> getRolePermissions() {
        return rolePermissions;
    }

    public void setRolePermissions(Set<RolePermission> rolePermissions) {
        this.rolePermissions = rolePermissions;
    }
}

3. 优化BaseEntity实现自动审计

如果希望created_at、updated_at自动填充,给BaseEntity添加JPA审计注解,并在启动类启用审计功能:

修改BaseEntity

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    @CreatedDate
    @Column(name = "created_at", nullable = false, updatable = false)
    private Instant createdAt;

    @LastModifiedDate
    @Column(name = "updated_at", nullable = false)
    private Instant updatedAt;

    @Column(name = "active")
    private Boolean active;

    // 保留getter/setter
}

启用JPA审计

在Spring Boot启动类添加注解:

@SpringBootApplication
@EnableJpaAuditing
public class YourApplication {
    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}

4. 调整控制器方法

更新权限分配逻辑,传入授权人信息并避免重复添加关联:

public Role assignPermission(UUID roleId, Permission permission, String grantedBy) {
    Role role = roleRepository.findById(roleId)
            .orElseThrow(() -> new IllegalArgumentException("Role not found"));
    
    // 检查是否已存在该权限关联,避免重复插入
    boolean isPermissionAssigned = role.getRolePermissions().stream()
            .anyMatch(rp -> rp.getPermission().getId().equals(permission.getId()));
    
    if (!isPermissionAssigned) {
        role.addPermission(permission, grantedBy);
        roleRepository.save(role);
    }
    return role;
}

关键说明

  • 这种方式完全由开发者控制关联表的所有字段,解决了原生@ManyToMany无法维护额外审计字段的问题。
  • 启用JPA审计后,created_at和updated_at会自动填充,无需手动赋值。
  • 添加权限前的重复检查可避免触发role_id+permission_id的唯一索引约束错误(如果表中已配置该索引)。

内容的提问来源于stack exchange,提问作者Gaurav Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 08:29:50