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

Spring Boot保存后MongoDB的DBRef字段roles为空问题排查

问题

我定义了User和Role两个模型,代码如下:

User模型:

@Document("user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @Id
    private String id;
    private String name;
    private String username;
    private String password;
    @DBRef
    private Collection<Role> roles = new ArrayList<>();
}

Role模型:

@Document("role")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
    @Id
    private String id;
    private String name;
}

同时实现了保存用户、保存角色、为用户添加角色的三个方法:

@Override
public User saveUser(User user) {
    log.info("Saving new user {} to the database", user.getName());
    return userRepository.save(user);
}

@Override
public Role saveRole(Role role) {
    log.info("Saving new role {} to the database", role.getName());
    return roleRepository.save(role);
}

@Override
public void addRoleToUser(String username, String roleName) {
    log.info("Adding role {} to user {}", roleName, username);
    User user = userRepository.findByUsername(username);
    Role role = roleRepository.findByName(roleName);
    user.getRoles().add(role);
}

通过CommandLineRunner初始化数据:

@Bean
CommandLineRunner run(UserService userService) {
    return args -> {
        userService.saveRole(new Role(null, "ROLE_USER"));
        userService.saveRole(new Role(null, "ROLE_ADMIN"));
        userService.saveUser(new User(null,"Stefan", "stefanadmin", "stefanadmin", new ArrayList<>()));
        userService.saveUser(new User(null,"Marko", "markoadmin", "markoadmin", new ArrayList<>()));
        userService.saveUser(new User(null,"Jovan", "jovanobicanuser", "jovanobicanuser", new ArrayList<>()));
        userService.addRoleToUser("stefanadmin", "ROLE_ADMIN");
        userService.addRoleToUser("stefanadmin", "ROLE_USER");
        userService.addRoleToUser("markoadmin", "ROLE_ADMIN");
        userService.addRoleToUser("jovanobicanuser", "ROLE_USER");
    };
}

所有操作执行正常,但返回用户数据时,roles字段为空数组,示例JSON:

{"id":"6331bda42c1fa17e41079c99","name":"Jovan","username":"jovanobicanuser","password":"jovanobicanuser","roles":[]}

教程使用MySQL而我使用MongoDB,推测问题出在关联方式上,请问可能的原因是什么?

分析与解决

原因1:添加角色后未持久化用户修改

MongoDB不会像MySQL那样自动同步内存中实体的修改,你在addRoleToUser方法里给用户添加角色后,没有调用userRepository.save(user)将修改保存到数据库,导致数据库中的用户角色列表仍是空的,查询自然返回空数组。

修改后的addRoleToUser方法:

@Override
public void addRoleToUser(String username, String roleName) {
    log.info("Adding role {} to user {}", roleName, username);
    User user = userRepository.findByUsername(username);
    Role role = roleRepository.findByName(roleName);
    user.getRoles().add(role);
    userRepository.save(user); // 新增该行,持久化用户的角色修改
}

原因2:@DBRef默认懒加载未触发关联加载

MongoDB的@DBRef注解默认是懒加载模式,如果查询用户时没有显式触发关联数据的加载,返回的User对象中roles字段会是空数组。可以通过两种方式解决:

  • 在用户查询方法上添加@Query(fields = "{'roles': 1}"),显式指定加载roles字段;
  • 或者修改@DBRef注解为立即加载:
@DBRef(lazy = false)
private Collection<Role> roles = new ArrayList<>();

原因3:初始化数据时的引用有效性问题(概率较低)

虽然当前初始化顺序是先存角色再存用户,但如果角色的保存未完成就去关联,可能导致引用无效。可以调整初始化代码,确保使用保存后返回的角色实例进行关联:

@Bean
CommandLineRunner run(UserService userService) {
    return args -> {
        // 先保存角色并获取实例
        Role userRole = userService.saveRole(new Role(null, "ROLE_USER"));
        Role adminRole = userService.saveRole(new Role(null, "ROLE_ADMIN"));
        
        // 保存用户并获取实例
        User stefan = userService.saveUser(new User(null,"Stefan", "stefanadmin", "stefanadmin", new ArrayList<>()));
        User marko = userService.saveUser(new User(null,"Marko", "markoadmin", "markoadmin", new ArrayList<>()));
        User jovan = userService.saveUser(new User(null,"Jovan", "jovanobicanuser", "jovanobicanuser", new ArrayList<>()));
        
        // 关联角色后重新保存用户
        stefan.getRoles().add(adminRole);
        stefan.getRoles().add(userRole);
        userService.saveUser(stefan);
        
        marko.getRoles().add(adminRole);
        userService.saveUser(marko);
        
        jovan.getRoles().add(userRole);
        userService.saveUser(jovan);
    };
}

内容的提问来源于stack exchange,提问作者Stefan Jankovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 03:25:23