【发布时间】:2021-06-21 04:05:14
【问题描述】:
fun main(args: Array<String>)
{
runApplication<JpaTest>(*args).getBean(JpaTest::class.java).test()
}
@SpringBootApplication
class JpaTest
{
@Autowired
lateinit var repository: PersonRepository
fun test()
{
repository.save(Person())
runBlocking {
suspend fun update(name: String, delay: Long)
{
val p = repository.findById(1).get()
delay(delay)
println("=== $name")
p.name = name
repository.save(p)
}
withContext(Dispatchers.Default) {
awaitAll(
async { update("Right Name", 3000) },
async { update("Wrong Name", 5000) }
)
println(" ")
val p = repository.findById(1).get()
println("Final: $p")
println(" ")
}
}
exitProcess(0)
}
}
@Repository
interface PersonRepository : CrudRepository<Person, Long>
@Entity
@DynamicUpdate
@OptimisticLocking(type=OptimisticLockType.DIRTY)
data class Person(
@Id
@GeneratedValue
val id: Long = 0,
var name: String = "?",
)
我正在尝试模拟一个多线程环境(或运行同一应用的多个实例)同时从数据库中获取一个实体,并对其进行更改和持久化。 p>
当第一个保存任务运行时(3 秒后):
=== Right Name
Hibernate: select person0_.id as id1_0_0_, person0_.name as name2_0_0_ from person person0_ where person0_.id=?
2021-03-24 09:18:38.565 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2021-03-24 09:18:38.566 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicExtractor : extracted value ([name2_0_0_] : [VARCHAR]) - [UNKNOWN]
Hibernate: update person set name=? where id=? and name=?
2021-03-24 09:18:38.567 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [Right Name]
2021-03-24 09:18:38.568 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2021-03-24 09:18:38.568 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [UNKNOWN]
当第二个保存任务运行时(5s 后):
=== Wrong Name
Hibernate: select person0_.id as id1_0_0_, person0_.name as name2_0_0_ from person person0_ where person0_.id=?
2021-03-24 09:18:40.556 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [BIGINT] - [1]
2021-03-24 09:18:40.556 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicExtractor : extracted value ([name2_0_0_] : [VARCHAR]) - [Right Name]
Hibernate: update person set name=? where id=? and name=?
2021-03-24 09:18:40.557 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [Wrong Name]
2021-03-24 09:18:40.557 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [BIGINT] - [1]
2021-03-24 09:18:40.557 TRACE 21791 --- [atcher-worker-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [Right Name]
请注意,两个任务都为同一个对象获取数据库,然后在更新/保存之前休眠了 3 秒/5 秒。
这里的问题是second task 在更新之前再次获取了数据库,这意味着first task 的更改丢失了。我尝试添加@SelectBeforeUpdate(false),但也没有用。
我期待当second task 尝试持久化实体时,会抛出OptimisticLockException。
如果我将 lockType 更改为 VERSION,它将按预期工作,并且 second task 的更新被拒绝。这是为什么呢?
【问题讨论】:
标签: java hibernate kotlin spring-data-jpa