最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

spring boot - Generated column isn't returned by save() when performing an update - Stack Overflow

programmeradmin3浏览0评论

I am a beginner learning about Spring Boot, and I've been having trouble making generated values work. I would like to have the database (Postgres) generate a timestamp column when a row is inserted, instead of having Spring generate it, but the problem is Spring doesn't seem to select the timestamp from the database when it performs an update.

When I save() a new entity, it is inserted fine in the database, a timestamp is generated and returned to Spring; everything works fine. When I try to find() an existing entity, the timestamp is also returned, so that's fine too. However when I try to update an existing entity, Spring DOESN'T access the timestamp that's in the database, and it instead returns null for the corresponding field.

Here is the entity definition:

import jakarta.persistence.*
import .hibernate.annotations.CurrentTimestamp
import .hibernate.generator.EventType
import java.time.OffsetDateTime

@Entity(name = "users")
@Table(name = "users")
data class User(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int?,

    val name: String,

    @Column(name = "date_created")
    @CreationTimestamp(source = SourceType.DB)
    val dateCreated: OffsetDateTime?,
)

Say the database already has a row like 1 | 2025-01-02 02:03:04 | user1. A PUT request like { name: "new user" } will update the name field in the database without changing the timestamp, but the updated entity returned by JPA will be { id: 1, name: "new user", dateCreated: null }. I'm not sure why that is. I know that JPA executes an additional SELECT when INSERTing a new row in order to get the timestamp that was generated by Postgres, but I don't see why it wouldn't just get the already existing timestamp when UPDATEing.

The controller and service classes for completeness:

import com.example.hello.User
import .springframework.http.HttpStatus
import .springframework.http.ResponseEntity
import .springframework.web.bind.annotation.*

@RestController
@RequestMapping(path = ["/users"])
class UserController(private val userService: UserService) {
    @PostMapping
    fun createStaff(@RequestBody user: User): ResponseEntity<User> {
        val createdUser = userService.create(user)
        return ResponseEntity(createdUser, HttpStatus.CREATED)
    }

    @PutMapping(path = ["/{id}"])
    fun updateUser(@PathVariable("id") id: Int, @RequestBody user: User): ResponseEntity<User> {
        val updatedUser = userService.update(id, user)
        return ResponseEntity(updatedUser, HttpStatus.OK)
    }

    @GetMapping(path = ["/{id}"])
    fun readUser(@PathVariable("id") id: Int): ResponseEntity<User> {
        val user = userService.get(id)
        return user?.let { ResponseEntity.ok(it) } ?: ResponseEntity(HttpStatus.NOT_FOUND)
    }
}
import com.example.hello.User
import com.example.hello.UserRepository
import .springframework.data.repository.findByIdOrNull
import .springframework.stereotype.Service

@Service
class UserService(private val userRepository: UserRepository) {
    fun create(user: User): User {
        return userRepository.save(user)
    }

    fun update(id: Int, user: User): User {
        val userWithId = user.copy(id = id)
        return userRepository.save(userWithId)
    }

    fun get(id: Int): User? {
        return userRepository.findByIdOrNull(id)
    }
}

I am a beginner learning about Spring Boot, and I've been having trouble making generated values work. I would like to have the database (Postgres) generate a timestamp column when a row is inserted, instead of having Spring generate it, but the problem is Spring doesn't seem to select the timestamp from the database when it performs an update.

When I save() a new entity, it is inserted fine in the database, a timestamp is generated and returned to Spring; everything works fine. When I try to find() an existing entity, the timestamp is also returned, so that's fine too. However when I try to update an existing entity, Spring DOESN'T access the timestamp that's in the database, and it instead returns null for the corresponding field.

Here is the entity definition:

import jakarta.persistence.*
import .hibernate.annotations.CurrentTimestamp
import .hibernate.generator.EventType
import java.time.OffsetDateTime

@Entity(name = "users")
@Table(name = "users")
data class User(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int?,

    val name: String,

    @Column(name = "date_created")
    @CreationTimestamp(source = SourceType.DB)
    val dateCreated: OffsetDateTime?,
)

Say the database already has a row like 1 | 2025-01-02 02:03:04 | user1. A PUT request like { name: "new user" } will update the name field in the database without changing the timestamp, but the updated entity returned by JPA will be { id: 1, name: "new user", dateCreated: null }. I'm not sure why that is. I know that JPA executes an additional SELECT when INSERTing a new row in order to get the timestamp that was generated by Postgres, but I don't see why it wouldn't just get the already existing timestamp when UPDATEing.

The controller and service classes for completeness:

import com.example.hello.User
import .springframework.http.HttpStatus
import .springframework.http.ResponseEntity
import .springframework.web.bind.annotation.*

@RestController
@RequestMapping(path = ["/users"])
class UserController(private val userService: UserService) {
    @PostMapping
    fun createStaff(@RequestBody user: User): ResponseEntity<User> {
        val createdUser = userService.create(user)
        return ResponseEntity(createdUser, HttpStatus.CREATED)
    }

    @PutMapping(path = ["/{id}"])
    fun updateUser(@PathVariable("id") id: Int, @RequestBody user: User): ResponseEntity<User> {
        val updatedUser = userService.update(id, user)
        return ResponseEntity(updatedUser, HttpStatus.OK)
    }

    @GetMapping(path = ["/{id}"])
    fun readUser(@PathVariable("id") id: Int): ResponseEntity<User> {
        val user = userService.get(id)
        return user?.let { ResponseEntity.ok(it) } ?: ResponseEntity(HttpStatus.NOT_FOUND)
    }
}
import com.example.hello.User
import com.example.hello.UserRepository
import .springframework.data.repository.findByIdOrNull
import .springframework.stereotype.Service

@Service
class UserService(private val userRepository: UserRepository) {
    fun create(user: User): User {
        return userRepository.save(user)
    }

    fun update(id: Int, user: User): User {
        val userWithId = user.copy(id = id)
        return userRepository.save(userWithId)
    }

    fun get(id: Int): User? {
        return userRepository.findByIdOrNull(id)
    }
}
Share Improve this question edited Feb 4 at 23:19 Ken White 126k15 gold badges236 silver badges466 bronze badges asked Feb 4 at 18:29 FinnCoalFinnCoal 651 silver badge6 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

When you call PUT api, you only pass name in the body, but the request User type, so Spring will understand that your body is:

{
  "id": null,
  "name": "your_input_name",
  "date_created": null
}

And in your update method, you only copy object with id => the dateCreated still null. That why when it save to db, the date_created column is null

You can first query the object in db with id, change name field then save it back.

Or you can add updatable = false to make sure date_created will not be updated.

@Column(name= "date_created", updatable = false)

Since the field is marked as nullable. And update does not do anything with createTimeStamp it is getting null data.

You can change your code to below.

import jakarta.persistence.*
import .hibernate.annotations.CurrentTimestamp
import .hibernate.generator.EventType
import java.time.OffsetDateTime

@Entity(name = "users")
@Table(name = "users")
data class User(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Int?,

    val name: String,

    @Column(name = "date_created", nullable = false)
    @CreationTimestamp(source = SourceType.DB)
    val dateCreated: OffsetDateTime,
)
发布评论

评论列表(0)

  1. 暂无评论