Spring Data JPA调用saveAllAndFlush仅插入单条角色数据问题排查
Spring Data JPA 数据插入与关联查询问题排查
一、Roles表仅插入1条记录的原因及修复
问题根因
你的Roles实体将userId设为唯一主键,而你生成的4条角色数据的userId完全相同(都属于同一个用户)。数据库主键具有唯一性约束,后续插入的同主键数据会直接覆盖之前的记录,最终只保留最后一条。
修复方案
根据业务需求选择以下两种方案:
- 方案1:给Roles添加独立主键(支持同一用户重复拥有相同角色)
适配你的JSON中存在重复角色的场景,修改Roles实体:
@Getter@Setter@Builder@AllArgsConstructor@NoArgsConstructor @Entity@Table(name="roles") public class Roles { @Id @GeneratedValue private UUID id; // 新增独立主键 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private Users user; // 关联Users实体,替代原userId字段 @Column(name="role_code",nullable=false) private String roleCode; @Column(name="system_type",nullable=false) private String systemType; private OffsetDateTime createdAt; // 保留原有字段 }
插入代码中,将userId(users1.getId())改为user(users1)即可。
- 方案2:使用复合主键(同一用户的角色唯一由roleCode+systemType决定)
若业务不允许同一用户重复拥有相同角色,可创建复合主键:
// 复合主键类,需实现Serializable并重写equals、hashCode @Embeddable @Getter@Setter@EqualsAndHashCode public class RolePK implements Serializable { @Column(name="user_id") private UUID userId; @Column(name="role_code") private String roleCode; @Column(name="system_type") private String systemType; } // 修改Roles实体 @Getter@Setter@Builder@AllArgsConstructor@NoArgsConstructor @Entity@Table(name="roles") public class Roles { @EmbeddedId private RolePK id; private OffsetDateTime createdAt; }
二、EntityGraph关联查询返回roles为空对象的原因及修复
问题根因
- 实体无关联关系:
Users实体中未定义与Roles的关联字段,@EntityGraph(attributePaths = "roles")中的roles属性不存在,Hibernate无法执行关联查询。 - 数据无法映射:即使数据库中有Roles数据,由于没有关联映射,无法自动封装到Users对象中;返回的空对象集合是因为DTO转换时创建了与原列表长度一致的空对象,但未填充属性。
修复方案
- 建立实体双向关联
在Users实体中添加@OneToMany关联:
@Getter@Setter@Builder@AllArgsConstructor@NoArgsConstructor @Entity@Table(name="users") public class Users { @Id@GeneratedValue private UUID id; @Column(name="login_name",unique=true,nullable=false) private String loginName; @Column(name="password",nullable=false) private String password; @Column(name="email",unique=true,nullable=false) private String email; @Column(name="status",nullable=false,length=10) @Enumerated(EnumType.STRING) private UserStatus status; @Column(name="full_name") // 补充代码中用到的fullName字段 private String fullName; // 添加与Roles的关联 @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List<Roles> roles = new ArrayList<>(); }
确保Roles实体使用方案1中的关联配置(@ManyToOne关联Users)。
验证关联查询
此时你的UserRepository中的findById方法可以正常通过@EntityGraph关联查询roles数据。检查DTO转换逻辑
确保将Users实体的roles集合正确映射到UserRequestDto的roles字段,示例手动转换:
UserRequestDto dto = UserRequestDto.builder() .loginName(users.getLoginName()) .email(users.getEmail()) .status(users.getStatus()) .roles(users.getRoles().stream() .map(role -> UserRoleDto.builder() .roleCode(role.getRoleCode()) .systemType(role.getSystemType()) .build()) .collect(Collectors.toList())) .build();
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

