Sou iniciante aprendendo sobre Spring Boot e tenho tido problemas para fazer valores gerados funcionarem. Gostaria que o banco de dados (Postgres) gerasse uma coluna de timestamp quando uma linha é inserida, em vez de o Spring gerá-la, mas o problema é que o Spring parece não selecionar o timestamp do banco de dados quando ele executa uma atualização.
Quando eu insiro save()
uma nova entidade, ela é inserida corretamente no banco de dados, um timestamp é gerado e retornado ao Spring; tudo funciona bem. Quando tento find()
uma entidade existente, o timestamp também é retornado, então está tudo bem também. No entanto, quando tento atualizar uma entidade existente, o Spring NÃO acessa o timestamp que está no banco de dados e, em vez disso, retorna nulo para o campo correspondente.
Aqui está a definição da entidade:
import jakarta.persistence.*
import org.hibernate.annotations.CurrentTimestamp
import org.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?,
)
Digamos que o banco de dados já tenha uma linha como 1 | 2025-01-02 02:03:04 | user1
. Uma solicitação PUT como { name: "new user" }
atualizará o name
campo no banco de dados sem alterar o timestamp, mas a entidade atualizada retornada pelo JPA será { id: 1, name: "new user", dateCreated: null }
. Não tenho certeza do porquê. Sei que o JPA executa um SELECT adicional ao INSERTing uma nova linha para obter o timestamp que foi gerado pelo Postgres, mas não vejo por que ele não obteria apenas o timestamp já existente ao UPDATEing.
As classes de controlador e serviço para integridade:
import com.example.hello.User
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.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 org.springframework.data.repository.findByIdOrNull
import org.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)
}
}
Quando você chama a API PUT, você não passa apenas
name
o corpo, mas também oUser
tipo de solicitação, então o Spring entenderá que seu corpo é:E no seu
update
método, você só copia o objeto com id => odateCreated
stillnull
. É por isso que quando ele salva no db, adate_created
coluna énull
Você pode primeiro consultar o objeto no banco de dados com
id
, alterarname
o campo e salvá-lo novamente.Ou você pode adicionar
updatable = false
para ter certeza dedate_created
que não será atualizado.Já que o campo está marcado como anulável. E update não faz nada com createTimeStamp, ele está obtendo dados nulos.
Você pode alterar seu código abaixo.