Spring Boot 我如何发送一个没有来自实体的键值的put请求?

sshcrbum  于 2022-11-05  发布在  Spring
关注(0)|答案(2)|浏览(147)

我试图更新一个用户,并将密码添加到数据库中创建的新列中,因为密码将被加密。但是,我收到了以下错误,它不是作为行的更新发送,而是作为数据库中的新行发送。我检查了一下,大多数情况下都是使用.保存(方法)在存储库中进行更新,但它更新的是对象,而不是添加新行:

2022-10-26 12:50:37.648  INFO 13732 --- [nio-8080-exec-3] com.ssc.test.cb3.service.UserService     
: Fetching user htorres
Existent user: User(serie=5, username=htorres, name=Herney Torres, password=htorres, 
profile=1, email=, status=false, passwordc=null, roles=[Role(serie=1, name=ROLE_USER)])
2022-10-26 12:50:37.793  INFO 13732 --- [nio-8080-exec-3] com.ssc.test.cb3.service.UserService     
: Saving new user Herney Torres to the database
2022-10-26 12:50:38.064  WARN 13732 --- [nio-8080-exec-3] 
o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: 23505
2022-10-26 12:50:38.064 ERROR 13732 --- [nio-8080-exec-3] 
o.h.engine.jdbc.spi.SqlExceptionHelper   : ERROR: llave duplicada viola restricción de 
unicidad «usuario_login_key»
Detail: Ya existe la llave (login)=(htorres).

Detail中的最后一条消息表示:已存在一个密钥(登录名)=(htorres)。
如果没有主键“login”(即用户名),我如何发送请求?我尝试在postman中不将其包含在请求中,但它返回错误ERROR 13732 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper:ERROR:〈〉列的null值违反了not null '的限制,我检查了列表是否可以使用.remove()来不包含主键,如以下资源所示,但不确定在我的体系结构中如何:
SpringRest with arrayLists
这是我的课:

package com.ssc.test.cb3.controller;

import com.ssc.test.cb3.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

/**
 * Class to handle REST services and APIs for the Customer's class
 *
 * @author ssc
 */
@RestController
@RequestMapping("/v1/users")
//@CrossOrigin(origins = "http://localhost:3000")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @PutMapping("/updateUser/{id}")
    private ResponseEntity<?> updateUser(@PathVariable("id") String username, @RequestBody User user){
        User existentUser = userService.getUser(username);
        System.out.println("Existent user: " + existentUser);
        existentUser.setPasswordc(passwordEncoder.encode(user.getPasswordc()));

        return ResponseEntity.ok(userService.createUser(user));   
    }

}

服务类:
软件包com.ssc.test.cb3.service;

import com.ssc.test.cb3.model.Role;
import com.ssc.test.cb3.model.User;
import com.ssc.test.cb3.repository.RoleRepository;
import com.ssc.test.cb3.repository.UserRepository;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Class to prepare the services to be dispatched upon request regarding Customers. 
 * @author ssc
 */

@Service 
@Transactional 
@Slf4j
@RequiredArgsConstructor
public class UserService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private final RoleRepository roleRepository;
    @Autowired
    private final PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);  // details from user loaded by getting the username
        if(user == null){
            log.error("User not found in the database");
            throw new UsernameNotFoundException("User not found in the database");
        } else {
            log.info("User found in the database: {}", username);
        }
        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
        user.getRoles().forEach(role -> { 
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        });
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
    }

    /**
     *  Method to get details from a user by the username
     * @param username key that helps to identify the user we want to retrieve from the database
     * @return the user details. 
     * @throws UsernameNotFoundException 
     */
    public User getUserDetailsByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);  // details from user loaded by getting the username
        if(user == null){
            log.error("User not found in the database by that username");
            throw new UsernameNotFoundException("User not found in the database by that username");
        } else {
            log.info("User found in the database: {}", username);
        }
        return user;
    }

    /**
     * 
     * @param id
     * @return 
     */
    public User getUser(String username){
        log.info("Fetching user {}", username);
        return userRepository.findByUsername(username);
    }

    /**
     * Functionality to create a new user
     * @param user receives an objet User to be saved on the database
     * @return the action of saving the user in the database. 
     */
    public User createUser(User user){
        log.info("Saving new user {} to the database", user.getName());
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        user.setPasswordc(passwordEncoder.encode(user.getPasswordc()));
        return userRepository.save(user);
    } 

}

我的存储库类:

package com.ssc.test.cb3.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.ssc.test.cb3.model.User;

/**
 * Class that extends to the repository for database management queries with table Usuario
 * @author ssc
 */

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {

    User findByUsername(String username);
}

最后是我的用户实体类:

package com.ssc.test.cb3.model;

import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * Class that models the entity User as table of the database
 * @author cardo
 */
@Entity
@Table(name = "usuario")
@Data @NoArgsConstructor @AllArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int serie;

    @Column(name = "login")  
    private String username;   // Username...

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

    private String password;

    @Column(name = "perfil")
    private int profile;

    private String email;

    @Column(name = "activo")
    private boolean status;

    private String passwordc;

    @ManyToMany(fetch = FetchType.EAGER)
    private Collection<Role> roles = new ArrayList<>();
}
t1rydlwq

t1rydlwq1#

你在做的事有猫腻。
首先,在控制器中执行此操作

User existentUser = userService.getUser(username);
        System.out.println("Existent user: " + existentUser);
        existentUser.setPasswordc(passwordEncoder.encode(user.getPasswordc()));

但不保存existingUser,因此更改不会持久。
然后,使用有效负载创建新用户

return ResponseEntity.ok(userService.createUser(user)); 

    public User createUser(User user){
        log.info("Saving new user {} to the database", user.getName());
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        user.setPasswordc(passwordEncoder.encode(user.getPasswordc()));
        return userRepository.save(user);
    }

但是它的有效载荷似乎包含了一个现有用户的信息。

public User createOrUpdateUser(User userPayload){
        log.info("Saving user {} to the database", user.getName());

         User dbUser = getUser(user.getUsername());

        if(isNull(dbUser)) {
          dbUser = ..logic for creating a new user
        } else {
          // logic to update the db user properties using the userPayload
          dbUser.set
        }

        return userRepository.save(user);
    }

另外,我会重新考虑你在这里做什么,因为这些可以在POST中划分为创建用户和PUT更新用户。

j9per5c4

j9per5c42#

主要的问题是您没有将现有的User对象传递给存储库的保存方法。数据库中的主键是自动生成的ID,而不是用户名。当您第一次将用户检索到existentUser变量中时,这是具有主键集的附加实体。在数据库事务范围内对此实体所做的任何更新都将自动保留回数据库。问题是您随后从初始请求传递了没有ID的非附加User对象,因此系统认为您正在尝试创建新用户,但是由于现在有一个重复的登录,所以它失败了。我认为要使它工作,您只需要删除userService.createUser(user)调用,并使用@Transactional注解updateUser控制器方法。您也应该将方法设为公用。
话虽如此,ALex建议创建一个upsert服务方法是个好主意,然后您可以将@Transactional注解移到该方法中,并将事务边界保留在服务中而不在控制器中。

相关问题