0
votes
@file:Suppress("DuplicatedCode")

package test

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.repository.CrudRepository
import javax.annotation.PostConstruct
import javax.persistence.*

fun main(args: Array<String>)
{
    runApplication<TestApplication>(*args)
}

@SpringBootApplication
class TestApplication(
    val personRepository: PersonRepository,
    val vehicleRepository: VehicleRepository
)
{
    @PostConstruct
    fun init()
    {
        var p1 = personRepository.save(Person(name = "Fred", age = 30))

        println(" ")
        p1.vehicles += vehicleRepository.save(Vehicle(type = "Car", brand = "BMW"))
        personRepository.save(p1)

        println(" ")
        p1.vehicles += vehicleRepository.save(Vehicle(type = "Car", brand = "BMW"))
        personRepository.save(p1)

        println(" ")
        p1.vehicles += vehicleRepository.save(Vehicle(type = "Car", brand = "BMW"))
        personRepository.save(p1)
    }
}

interface PersonRepository : CrudRepository<Person, Long>
interface VehicleRepository : CrudRepository<Vehicle, Long>

@Entity
data class Person(
    @Id
    @GeneratedValue
    val id: Long = 0,

    val name: String,

    val age: Int,

    @OneToMany
    @OrderColumn
    @JoinColumn(name = "personId")
    val vehicles: MutableList<Vehicle> = ArrayList()
)

@Entity
data class Vehicle(

    @Id
    @GeneratedValue
    val id: Long = 0,

    var type: String,

    var brand: String
)

Log

Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into person (age, name, id) values (?, ?, ?)

Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into vehicle (brand, type, id) values (?, ?, ?)
Hibernate: select [...]
Hibernate: select [...]
Hibernate: update vehicle set person_id=?, vehicles_order=? where id=?

Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into vehicle (brand, type, id) values (?, ?, ?)
Hibernate: select [...]
Hibernate: select [...]
Hibernate: update vehicle set person_id=?, vehicles_order=? where id=?
Hibernate: update vehicle set person_id=?, vehicles_order=? where id=?

Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into vehicle (brand, type, id) values (?, ?, ?)
Hibernate: select [...]
Hibernate: select [...]
Hibernate: update vehicle set person_id=?, vehicles_order=? where id=?
Hibernate: update vehicle set person_id=?, vehicles_order=? where id=?
Hibernate: update vehicle set person_id=?, vehicles_order=? where id=?

Question

Each car that is added, hibernate is updating all the other cars again. I noticed that if I change personRepository.save(p1) to p1 = personRepository.save(p1), it works as expected and only updates the new car. Why is that? Does hibernate has some internal "commit id"?

Also, how could I transform this code into a transaction?

Thanks.

1

1 Answers

1
votes

Each call to save is calling EntityManager#merge which flushes the current object state to the database, possibly returning you a new object which is then "managed" meaning that changes done to that object will be flushed at the end of the transaction automatically. You can run that code in a transaction by using the @Transactional annotation, but not sure if that also works for @PostConstruct. Within the transaction, every object that you save is managed, so the changes are flushed at the end of the transaction automatically. You could use code like the following:

@PostConstruct
@Transactional
fun init()
{
    var p1 = personRepository.save(Person(name = "Fred", age = 30))
    p1.vehicles += vehicleRepository.save(Vehicle(type = "Car", brand = "BMW"))
    p1.vehicles += vehicleRepository.save(Vehicle(type = "Car", brand = "BMW"))
    p1.vehicles += vehicleRepository.save(Vehicle(type = "Car", brand = "BMW"))
}