3

I can't understand, what's wrong with my Service. I receive org.hibernate.StaleObjectStateException trying to run this method:

fun updateNameForPhone(phone: String, name: String): Client {
    val res = clientRepository.findByPhone(phone) ?: throw ClientNotFoundException(phone)

    res.name = name
    return clientRepository.save(res)
}

ClientRepository:

@Repository
interface ClientRepository : JpaRepository<Client, UUID> {

    fun findByPhone(phone: String): Client?
}

Client entity:

@Entity
data class Client(
        var name: String = "",
        var phone: String = "",
        @Id @GeneratedValue(strategy = GenerationType.AUTO)
        val uuid: UUID = defaultUuid()
)

Exception:

Object of class [com.app.modules.client.domain.Client] with identifier [12647903-7773-4f07-87a8-e9f86e99aab3]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.app.modules.client.domain.Client#12647903-7773-4f07-87a8-e9f86e99aab3]"

What is the reason? I'm using Kotlin 1.3.11, Spring Boot 2.1.1, MySql. I don't run it in different threads, just trying with single request.

Kamo Spertsian
  • 785
  • 2
  • 8
  • 23
  • can you share how have you defined the transaction here? – Ankur Jan 06 '19 at 10:48
  • Also, it would be helpful to have database table structure as well – Ankur Jan 06 '19 at 10:50
  • You get a optimistic lock exception but I don't see that you are using a version field. Can you please post the whole code? – Simon Martinelli Jan 06 '19 at 11:14
  • @simonmartinelli I have no version field, it has not yet been necessity. – Kamo Spertsian Jan 06 '19 at 21:28
  • @ankur No transaction and table creations myself, it's incapsulated by Spring. I just tag my Client class with Entity annotation. Service method is called from Controller. There is no interesting code there. – Kamo Spertsian Jan 06 '19 at 21:30
  • Here is what i think is the problem: clientRepository.findByPhone(phone) is taking place under transaction T1 while clientRepository.save(res) is taking place under transaction T2. since, your `res` object is part of 2 different transactions, you are facing this issue. Have you used @Transactional anywhere in your code? – Ankur Jan 07 '19 at 05:36
  • @Ankur Controller's method, which calls service method, is annotated as `Transactional`, but I've deleted this annotation from there and nothing've changed. – Kamo Spertsian Jan 09 '19 at 07:20
  • The problem disappears when I change the table identifier type from `UUID` to `Long`.. Looks like something is wrong with UUID and Spring communication.. Btw Long ids are not suitable for me, so I am trying to solve this problem. – Kamo Spertsian Jan 10 '19 at 14:40

1 Answers1

5

Well, finally I've found a solution. Better say workaround.

The problem is in the way spring uses UUID as entity identifier. So there are two workarounds, solving this issue:

  • first, you can change your id field type to other one, such as Long, for example, if it's possible to you;
  • or you can add this annotation to your uuid field: @Column(columnDefinition = "BINARY(16)").

The last solution I've found from this question.

Kamo Spertsian
  • 785
  • 2
  • 8
  • 23