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
相关产品推荐
相关产品推荐

