简体   繁体   中英

How to pass nested JSON with only ID in the second Spring Boot

I have this JSON to create a new User with an existing Account (I only pass the attribute accountId):

{
    "account": {
            "accountId":"06xxxx-5fxx-4xxx-xxxx-xxxxxx"
    },
    "userName":"user3",
    "emailAddress":"user3@gmail.com",
  "password":"123",
    "enabled":"1",
    "lastLogin":"2020-07-28"
}

My User model is the following:

public class User implements Serializable {

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Size(max = 36)
    @Column(name = "userId")
    private String userId;

    @JoinColumn(name = "accountId", referencedColumnName = "accountId", nullable = false)
    @ManyToOne
    private Account account;

    @NaturalId
    @Column(name = "userName")
    private String userName;

    @Column(name = "emailAddress")
    private String emailAddress;

    @Column(name = "password")
    private String password;

    @Column(name = "enabled")
    private Integer enabled;

    @Column(name = "lastLogin")
    private String lastLogin;

    @OneToMany(
            mappedBy = "user",
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private List<UserGroupUser> userGroups = new ArrayList<>();

//Constructor, Getters and setters

}

The class Account is the first instance to create before User:

public class Account implements Serializable {

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Size(max = 36)
    @Column(name = "accountId")
    private String accountId;

    @Column(name = "accountName")
    private String accountName;

    @Column(name = "company")
    private String company;

    @Column(name = "address")
    private String address;

    @Column(name = "emailAddress")
    private String emailAddress;

    @Column(name = "dicomId")
    private String dicomId;

    @Column(name = "enabled")
    private Integer enabled;

    @OneToMany (mappedBy = "account",
                cascade = CascadeType.ALL,
                orphanRemoval = true)
    Set<User> users = new HashSet<>();

    @OneToMany (mappedBy = "account",
                cascade = CascadeType.ALL,
                orphanRemoval = true)
    Set<UserGroup> userGroups = new HashSet<>();

    //Constructor, Getters and setters
}

I have to do that with Spring Boot. What is the most factible for these cases, please?

This is my exception message, as it follows:

{
  "timestamp": "2020-07-29T08:00:05.608+0000",
  "message": "The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!",
  "details": "uri=/api/v1/addUser"
}

Spring Boot Console generated:

2020-07-29 11:38:43.141  INFO 14172 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9898 (http) with context path ''
2020-07-29 11:38:43.144  INFO 14172 --- [  restartedMain] c.p.conexiona.ConexionaApplication       : Started ConexionaApplication in 6.526 seconds (JVM running for 8.967)
2020-07-29 11:38:43.146 DEBUG 14172 --- [  restartedMain] o.s.boot.devtools.restart.Restarter      : Creating new Restarter for thread Thread[main,5,main]
2020-07-29 11:38:43.147 DEBUG 14172 --- [  restartedMain] o.s.boot.devtools.restart.Restarter      : Immediately restarting application
2020-07-29 11:38:43.147 DEBUG 14172 --- [  restartedMain] o.s.boot.devtools.restart.Restarter      : Created RestartClassLoader org.springframework.boot.devtools.restart.classloader.RestartClassLoader@10c515a9
2020-07-29 11:38:43.147 DEBUG 14172 --- [  restartedMain] o.s.boot.devtools.restart.Restarter      : Starting application com.practicas.conexiona.ConexionaApplication with URLs [file:/C:/Users/canro/IdeaProjects/project_example/target/classes/]
2020-07-29 11:38:56.911  INFO 14172 --- [nio-9898-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-07-29 11:38:56.912  INFO 14172 --- [nio-9898-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-07-29 11:38:56.912 DEBUG 14172 --- [nio-9898-exec-1] o.s.web.servlet.DispatcherServlet        : Detected StandardServletMultipartResolver
2020-07-29 11:38:56.920 DEBUG 14172 --- [nio-9898-exec-1] o.s.web.servlet.DispatcherServlet        : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2020-07-29 11:38:56.920  INFO 14172 --- [nio-9898-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 8 ms
2020-07-29 11:38:56.930 DEBUG 14172 --- [nio-9898-exec-1] o.s.web.servlet.DispatcherServlet        : POST "/api/v1/addUser", parameters={}
2020-07-29 11:38:56.933 DEBUG 14172 --- [nio-9898-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.rest.UserController#createUser(User)
2020-07-29 11:38:57.030 DEBUG 14172 --- [nio-9898-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Read "application/json;charset=UTF-8" to [com.example.model.User@2802b333]
null
2020-07-29 11:38:57.427 DEBUG 14172 --- [nio-9898-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Using @ExceptionHandler com.example.exception.GlobalExceptionHandler#globleExcpetionHandler(Exception, WebRequest)
2020-07-29 11:38:57.446 DEBUG 14172 --- [nio-9898-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json]
2020-07-29 11:38:57.447 DEBUG 14172 --- [nio-9898-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Writing [com.example.exception.ErrorDetails@4ddbc615]
2020-07-29 11:38:57.466  WARN 14172 --- [nio-9898-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.InvalidDataAccessApiUsageException: The given id must not be null!; nested exception is java.lang.IllegalArgumentException: The given id must not be null!]
2020-07-29 11:38:57.466 DEBUG 14172 --- [nio-9898-exec-1] o.s.web.servlet.DispatcherServlet        : Completed 500 INTERNAL_SERVER_ERROR

UserController:

@RestController
@RequestMapping("/api/v1")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users")
    public List<User> getAllUsers() {
        return userService.findAllUsers();
    }

    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUserById(
            @PathVariable(value = "id") String userId) throws ResourceNotFoundException {
        User user = userService.findUserById(userId)
                .orElseThrow(() -> new ResourceNotFoundException("user not found on :: "+ userId));
        return ResponseEntity.ok().body(user);
    }

    @PostMapping("/addUser")
    public User createUser(@Valid @RequestBody User user) { return userService.addUser(user); }

    @PutMapping("/users/{id}")
    public ResponseEntity<User> updateUser(
            @PathVariable(value = "id") String userId,
            @Valid @RequestBody User userDetails) throws ResourceNotFoundException {
        User user = userService.findUserById(userId)
                .orElseThrow(() -> new ResourceNotFoundException("user not found on :: "+ userId));

        final User updatedUser = userService.updateUser(userDetails, user);
        if  (updatedUser == null){
            return new ResponseEntity<User>(HttpStatus.EXPECTATION_FAILED);
        } else {
            return ResponseEntity.ok(updatedUser);
        }
    }

    @DeleteMapping("/user/{id}")
    public Map<String, Boolean> deleteUser(
            @PathVariable(value = "id") String userId) throws Exception {
        User user = userService.findUserById(userId)
                .orElseThrow(() -> new ResourceNotFoundException("user not found on :: "+ userId));

        userService.deleteUser(user);
        Map<String, Boolean> response = new HashMap<>();
        response.put("deleted", Boolean.TRUE);
        return response;
    }

}

UserService:

@Service
public class UserService implements IUserService{
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private AccountRepository accountRepository;

    @Override
    public List<User> findAllUsers() {
        return (List<User>) userRepository.findAll();
    }

    @Override
    public Optional<User> findUserById(String userId){
        return userRepository.findById(userId);
    }

    @Override
    public User addUser(User user){
        User userExisted = userRepository.findById(user.getUserId()).get();
        Account accountExisted = accountRepository.findById(user.getAccount().getAccountId()).get();

        if(userExisted != null || accountExisted == null){
            return null;
        } else {
            user.setAccount(accountExisted);
            return userRepository.save(user);
        }
    }

    @Override
    public User updateUser(User userDetails, User user){
        Account accountExisted = accountRepository.findById(userDetails.getAccount().getAccountId()).get();

        if( accountExisted == null){
            return null;
        } else {
            user.setEmailAddress(userDetails.getEmailAddress());
            user.setUserName(userDetails.getUserName());
            user.setAccount(userDetails.getAccount());
            user.setPassword(userDetails.getPassword());
            user.setAccount(accountExisted);

            return userRepository.save(user);
        }
    }

    @Override
    public void deleteUser (User user){
        userRepository.delete(user);
    }
}

Within your addUser method, you are attempting to load a user with its userId :

@Override
public User addUser(User user){
    User userExisted = userRepository.findById(user.getUserId()).get();
    // ...
}

Whilst this would be fine technically speaking, it won't work when you know that the user argument is not yet persisted and thus has no generated id yet .

You should:

  • Either update you method implementation to check if the user input already holds an userId attribute (if not, you assume the user has not been created yet):

     @Override public User addUser(User user){ User userExisted = null; if (user.getUserId().= null) { userExisted = userRepository.findById(user.getUserId());get(). } //... }
  • Update your addUser method to search for a user based on other unique predicates (username, email or both).

Note that the second option would be the most suitable one and adheres to the RESTful architecture as attempting to create a user with an existing user id should already result on an error (that you should translate based on your application needs).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM