Spring data/Hibernate: detached entity passed to persist

1.1k Views Asked by At

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.

  • Why is it detached? I had assumed the @Transactional annotation on the method would make sure everything happens within the same persistence context, and this should not happen.
  • How can I prevent this? Is there a way without having to remove the cascaed option
  • Is there any good information source on how spring/jpa and hibernate play to gether - so far anything i found didn't really help me understand the concepts in detail, and how the respositoryies appear to hide the entity management and session concepts that you find, when trying to read up on hibernate.
1

There are 1 best solutions below

1
On BEST ANSWER

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.