指定为非空的参数为空:无法更新 Spring Boot jpa 中的单个实体字段。导致指定为非空的参数为空

Posted

技术标签:

【中文标题】指定为非空的参数为空:无法更新 Spring Boot jpa 中的单个实体字段。导致指定为非空的参数为空【英文标题】:Parameter specified as non-null is null:Can't Update individual Entity field in Spring Boot jpa. Caused Parameter specified as non-null is null 【发布时间】:2021-06-01 00:06:30 【问题描述】:

最近,我正在学习使用 Postgrasql 作为数据库的 Spring Boot。我所有的 POST、GET、DELETE 方法都可以在没有 PUT 方法的情况下完美运行。我想要的是更新实体的单个字段。但是如果我不更新所有字段,它会说“指定为非空的参数为空”

我的代码是

User.kt

import java.time.LocalDate
import javax.persistence.*
import javax.persistence.GenerationType.SEQUENCE

@Entity
@Table(name = "user_info")
data class User(
    @Id
    @SequenceGenerator(
        name = "user_seq",
        sequenceName = "user_seq",
        allocationSize = 1
    )
    @GeneratedValue(
        strategy = SEQUENCE,
        generator = "user_seq"
    )
    @Column(
        name = "id",
        updatable = false
    )
    val id: Long = -1,

    @Column(
        name = "first_name",
        nullable = false,
        length = 50,
        updatable = true
    )
    val firstName: String,

    @Column(
        name = "last_name",
        nullable = false,
        length = 50,
        updatable = true
    )
    val lastName: String,

    @Column(
        name = "email",
        nullable = true,
        length = 150,
        updatable = true
    )
    val email: String,

    @Column(
        name = "gender",
        nullable = false,
        length = 2,
        updatable = true
    )
    val gender: String,

    @Column(
        name = "date_of_birth",
        nullable = false,
        updatable = true
    )
    val dateOfBirth: LocalDate,

    @Column(
        name = "country",
        nullable = false,
        length = 50,
        updatable = true
    )
    val country: String
)

UserController.kt

包 com.example.demo.user

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.format.annotation.DateTimeFormat.ISO.DATE
import org.springframework.web.bind.annotation.*
import java.time.LocalDate

@RestController
@RequestMapping(
    path = [
        "/api/v1/"
    ]
)
class UserController(
    @Autowired private val userService: UserService
) 

    @PutMapping("update/id")
    fun updateUser(
        @PathVariable("id") id: Long,
        @RequestParam(required = false) firstName: String,
        @RequestParam(required = false) lastName: String,
        @RequestParam(required = false) email: String,
        @RequestParam(required = false) gender: String,
        @RequestParam(required = false) country: String,
        @RequestParam(required = false) @DateTimeFormat(iso = DATE) dateOfBirth: LocalDate
    ) 
        return userService.updateUser(id, firstName, lastName, email, gender, country, dateOfBirth)
    

UserService.kt

package com.example.demo.user

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate

@Service
class UserService(
    @Autowired private val userRepository: UserRepository
) 

    fun registerUser(user: User) 
        if (userRepository.findUserByEmail(user.email).isPresent) throw IllegalStateException("Email Already taken")
        userRepository.save(user)
    

    fun getUsers(offset: Int): List<User> 
        return userRepository.getUsers(offset)
    

    fun getUserInfo(userId: Long): User 
        if (!userRepository.existsById(userId)) throw IllegalArgumentException("User not found")
        return userRepository.findById(userId).get()
    

    fun deleteUser(userId: Long) 
        if (!userRepository.existsById(userId)) throw IllegalStateException("User with id $userId does not exist")
        userRepository.deleteById(userId)
    

    @Transactional
    fun updateUser(id: Long, firstName: String, lastName: String, email: String, gender: String, country: String, dateOfBirth: LocalDate) 
        val user = userRepository.findById(id).orElseThrow  throw IllegalStateException("User with id $id does not exist") 

//        if (userRepository.findUserByEmail(user.email).isPresent) throw IllegalStateException("Email Already taken")
        if (firstName.isNotEmpty()) user.firstName = firstName
        if (lastName.isNotEmpty()) user.lastName = lastName
        if (email.isNotEmpty()) user.email = email
        if (gender.isNotEmpty() && (gender == "M" || gender == "F")) user.gender = gender
        if (country.isNotEmpty()) user.country = country
        if (dateOfBirth.toString().isNotEmpty()) user.dateOfBirth = dateOfBirth
    


响应负载是


    "timestamp": "2021-03-02T11:45:27.025+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Parameter specified as non-null is null: method com.example.demo.user.UserController.updateUser, parameter dateOfBirth",
    "path": "/api/v1/update/1000"

【问题讨论】:

可以添加用户服务代码吗? @AnthonyChatellier 添加了。请检查 【参考方案1】:

问题来自您的 REST 控制器。请求参数使用 required=false 注释,但类型为 String。因此,当 HTTP 请求带有空值时,Spring 无法反序列化这些字段中的数据。 (这解释了 NullpointerException。

您必须在您的控制器中提供可空类型(如“字符串?”),并且这种可空性不应该在代码的所有层中激增。

例如,“名字”对于更新是可选的,但对于创建是必需的。

【讨论】:

感谢您的回复。在我的控制器类中添加可空类型后,我得到了“内部服务器错误”【参考方案2】:

我在 kotlin 上工作不多,但我认为问题在于 Kotlin 是 null-safe 编程语言。您需要明确告知变量是否可以为空。有关更多详细信息,请参阅此blog。使用接受 null 的字段更新您的实体类应该可以工作。

例如要允许空值,可以将变量声明为可空字符串,写成String?:

【讨论】:

感谢您的回复。在我的控制器类中添加可空类型后,我得到了“内部服务器错误” 为控制器和实体添加可空性

以上是关于指定为非空的参数为空:无法更新 Spring Boot jpa 中的单个实体字段。导致指定为非空的参数为空的主要内容,如果未能解决你的问题,请参考以下文章

java.lang.IllegalArgumentException:指定为非空的参数为空:方法 kotlin.jvm.internal.Intrinsics.checkParameterIsNotN

Kotlin 自定义对话框参数指定为非空

Graphiql 中标记为非空的所有字段和参数:Sangria,scala

Spring数据JPA和可以为空的参数

SpringBoot 接收Get请求个别参数可能为空的解决方案

java.security.InvalidAlgorithmParameterException:Linux上的trustAnchors参数必须为非空,或者为啥默认的信任库为空[重复]