hibernatejpaspring-data-jpaspring-kotlin

Spring data/Hibernate: detached entity passed to persist


I get "detached entity passed to persist", but I do not understand, how the object in question can be in a detached state.
Here is some context first.

two JpaRepositorys for each users and roles:

@Repository
interface RoleRepository : JpaRepository<UserRole, Long> {
    fun findByName(name: String): UserRole?
}

@Repository
interface BackendUserRepository : JpaRepository<BackendUser, Long> {
    fun findByUserName(name: String): BackendUser?
}

The BackendUser entity - besides the name - has several fields that should not be related to this question, the foreign field role comes from its base class:

abstract class User(
        @ManyToOne(fetch = FetchType.LAZY, cascade = arrayOf(CascadeType.ALL), optional = false)
        @JoinColumn(referencedColumnName = "name", nullable = false)
        var role: UserRole
) : UserDetails {
    // ...
}

On application startup, I want to ensure that an admin user exists in this ApplicationRunner:

@Component
class InitialDataApplicationRunner : ApplicationRunner {
    @Autowired
    lateinit var roles: RoleRepository
    @Autowired
    lateinit var users: BackendUserRepository

    override fun run(args: ApplicationArguments) {
        // some other stuff

        createAdminUser()
    }

    @Transactional
    private fun createAdminUser() {
        if (users.findByUserName("admin") == null) {
            val adminRole = roles.findByName("admin")!!
            val adminUser = BackendUser("admin", adminRole
                 // some more data here
            )
            users.save(adminUser)
        }
    }
}

The application throws an exception on startup and shuts down:

org.hibernate.PersistentObjectException: detached entity passed to persist: my.package.name.user.UserRole

The way I understand it here is, that adminRole is in a detached state straight after it was retrieved from the repository.


Solution

  • Since you have @Transactional on private method and also one called from the same class, there is no transaction and therefore detached state for save (see this). Not sure if you can apply @Transactional on run() or this class. Anyway, maybe you should create new "Service" class with public @Transactional method, and than call this service from your class.