为什么 Hibernate 在没有更改的情况下将实体标记为脏?

问题描述 投票:0回答:1

我正在使用 Hibernate 和 JPA。 我有一个看起来像这样的实体:

@Data
@Entity
@Table(name = "MY_ENTITY")
public class MyEntity {

    @Id
    @Column(name = "ID")
    public Long id;

    @Version
    @Column(name = "VERSION")
    public Long version = 1L;

    @Embedded
    @AttributeOverride(name = "timestamp", column = @Column(name = "UPDATED_TIME"))
    public CustomTimestamp updatedTime;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "ENTITY_CONNECTION", joinColumns = @JoinColumn(name = "ENTITY_ID"))
    @OrderColumn(name = "CONNECTION_ORDER")
    @Fetch(FetchMode.SUBSELECT)
    @JsonDeserialize(using = ConnectionDeserializer.class)
    public List<Connection> connections = new ArrayList<>();

    @PreUpdate
    public void preUpdate() {
        this.updatedTime = new CustomTimestamp();
    }

}
@Data
@Embeddable
public class Connection {

    @Column(name = "CONNECTED_ENTITY_ID")
    public String connectedEntityId;

    @Column(name = "CONNECTED_ENTITY_TYPE")
    public String connectedEntityType;

    @Column(name = "NOTE")
    public String note;


}

实体的输入包括带有删除标志的所有字段。提到的反序列化器获取现有列表并与发送的更新进行比较。更新列表中的现有实体,添加新实体并删除标有“删除”标志的实体。

在 API 中,我公开了“修补”实体的路径,这意味着用户传递他们想要更新的字段。我执行以下逻辑:

MyEntity originalEntity = repository.findById(id);
ObjectReader entityForUpdate = objectMapper.readerForUpdating(originalEntity);
MyEntity updatedEntity = objectMapper.update(entityForUpdate, updateInput);
repository.saveAndFlush(updatedEntity);

示例“补丁”输入类似于:

patch /entity/{id}
body: 
{
    "connections": [
        {
            "connectedEntityId": "test",
            "connectedEntityType": "testType"
        }
    ]
}

发送两次时,我希望第二次不会检测到任何“脏”内容,因此不会增加版本,但确实如此。

查看 Hibernate 的日志时,看起来“连接”集合很脏。

Hibernate的脏检查是如何判断集合是否脏的?

尝试使用 Set 而不是 ArrayList - Collection 仍然很脏。

尝试使用 OneToMany 映射 - 问题是相反的 - 父实体永远不会更新。据我了解,这不是最好的选择,因为“连接”本身并不作为一个实体。

尝试将生成的 ID 添加到连接实体 - 集合仍然脏。

已验证所有实体均按预期实施

equals()

java hibernate spring-data-jpa
1个回答
0
投票

Hibernate ORM 不会检查集合元素是否深度等于初始状态的元素。 为了避免不必要的更新语句,您必须避免在这种情况下替换对象。

使用 ObjectMapper,我的解决方案是重写默认设置器,如下所示:

public void setConnections(List<Connection> connections) {
    if (!CollectionUtils.isEqualCollection(this.connections, connections)) {
        this.connections.clear();
        this.connections.addAll(connections);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.