传递给持久化的分离实体 - 在 findByMethod 上

detached entity passed to persist - on findByMethod

我理解错误信息并且我知道如何解决它,但我想知道为什么它会出现在这个特定的地方,尤其是在 find 方法上。 我为此创建了一个迷你示例。

我有三个实体:

@Entity
data class Animal(
    var name: String,
    @ManyToOne(cascade = [CascadeType.ALL]) val zoo: Zoo) {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Int = -1
}

@Entity
data class Zoo(
    var city: String,
    @OneToMany(cascade = [CascadeType.ALL]) val employee: MutableList<Person>) {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Int = -1
}

@Entity
data class Person(var age: Int) {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Int = -1
}

服务:

   @Transactional
    fun save(name:String, city: String){
        repo.findByZooCity(city).ifPresent {
            it.zoo.employee.add(Person(22))
        }
        repo.findByZooCity("OTHER STRING!!!!").ifPresent { println("FOUND!") }
        repo.save(Animal(name, Zoo(city, mutableListOf(Person(33)))))
    }

回购:

interface AnimalRep: JpaRepository<Animal, Int>{

    fun findByZooCity(name: String): Optional<Animal>
}

通话:

  animalService.save("Zoo1", "Animal1")
  animalService.save("Zoo1", "Animal1")

异常: 在第二次调用时,我在 repo.findByZooCity("OTHER STRING!!!!") 上收到“传递给持久化的分离实体:com.example.Person”。我知道发生这种情况是因为我之前添加了一个“超然”的人。但为什么它会出现在 findBy 上? (连结果都没有?)

有脏检查吗?

感谢您的宝贵时间和帮助。

此行为取决于 FlushMode of EntityManager
FlushMode 定义何时将新实体和您对现有实体的更改写入数据库,换句话说,定义何时执行 flush() 操作。

The flush process synchronizes database state with session state by detecting state changes and executing SQL statements.

JPA 规范将 FlushModeType.AUTO 定义为默认刷新模式。它会在下一种情况下刷新持久性上下文:

  • 事务提交之前
  • 在执行使用任何数据库的查询之前 table,您的持久性上下文包含任何未决更改。

根据documentation

AUTO
The Session is sometimes flushed before query execution in order to ensure that queries never return stale state. This is the default flush mode.

总结:
因此在您的示例中,我们有默认的 FlushMode.AUTO,在查询执行框架期间执行 flush() 操作,这就是原因。


FlushMode.AUTO 正在基于查询修复一致性问题,但另一方面可能会导致不可预测的 table 刷新操作,并因此在大型服务的情况下出现性能问题.当您不需要解决一致性问题时,我建议将 FlushMode 更改为 COMMITFlushModeType.COMMIT 要求在提交事务之前刷新,但没有定义在执行查询之前需要发生什么。执行任何查询都不会刷新任何挂起的更改。

改变FlushMode:
1.全局为服务器添加属性

spring.jpa.properties.org.hibernate.flushMode=COMMIT

2。为特定查询设置

Query quey = entityManager.createQuery("SELECT * from Foo");
quey.setFlushMode(FlushModeType.COMMIT);

3。设置为 Session
EntityManager 注入您的服务。

   @Transactional
    fun save(name:String, city: String){
        entityManager.setFlushMode(FlushModeType.COMMIT)
        ...
    }

详情见 How does AUTO flush strategy work in JPA and Hibernate