Java Spring JPA:当我显式更新托管实体时,为什么它会尝试插入未初始化的行?

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

考虑以下实体:

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

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "myEntitySeq")
    @SequenceGenerator(name = "myEntitySeq", sequenceName = "my_entity_seq")
    private Long id;

    @OneToOne(optional = false)
    @JoinColumn(name = "other_id")
    private OtherEntity other;

    @Column(nullable = false)
    private String fieldA;

    @Column(nullable = false)
    private byte[] fieldB;

    public MyEntity() {}

    public MyEntity(OtherEntity other) {
        this.other = other;
    }

    // public getter/setters for all fields...
}

所有数据库字段都应为非空。

然后我有一个应该创建或更新此实体的事务方法:

    @Transactional
    public void createOrUpdate(OtherEntity other, String fieldA, byte[] fieldB) {
        var myEntity = myEntityRepository.findByOther(other)
            // Create and get a new managed entity, missing fields will be updated right after:
            .orElseGet(() -> entityManager.merge(new MyEntity(other)))
        myEntity.setFieldA(fieldA);
        myEntity.setFieldB(fieldB);
        // Once the transaction ends I expect the new entity to be INSERTed, otherwise UPDATEd
    }

但是此代码失败并显示错误,指出它无法插入新实体,因为它尝试将空字段插入表中(调用合并时,

fieldA
fieldB
都是空,即使我稍后更新状态)。

我的理解是,由于 SQL 查询被推迟到事务结束,因此首先调用 merge 来获取托管实例,然后更新状态将正确地推迟最后的完整插入。

相反,它似乎推迟了一个不完整的

INSERT
,然后是一个
UPDATE
,这使数据库
NOT NULL
对不完整插入的约束......

删除

NOT NULL
约束可以让前面的代码不会失败,因为它允许不完整的插入通过,并且以某种方式它还使用后面设置的属性更新行。但这意味着我的数据库架构允许在我不想要的地方使用空值。

我期望

entityManager.merge(...)
后面跟着集合将所有内容分组到单个
INSERT
语句中是错误的吗?是否期望执行
merge
会使用此时提供的状态安排
INSERT
,并且仅在稍后
UPDATE
使用稍后在事务中更改的状态?

java spring hibernate jpa
1个回答
0
投票

为了避免部分

INSERT
,我最终写的是推迟未初始化实体的合并,直到字段全部设置完毕:

    @Transactional
    public void createOrUpdate(OtherEntity other, String fieldA, byte[] fieldB) {
        var myEntity = myEntityRepository.findByOther(other)
            // Create a new unmanaged entity
            .orElseGet(() -> new MyEntity(other))
        myEntity.setFieldA(fieldA);
        myEntity.setFieldB(fieldB);
        entityManager.merge(myEntity);
    }

或者,在本例中,我们似乎可以使用

myEntityRepository.save(myEntity)
代替
entityManager.merge(myEntity)

© www.soinside.com 2019 - 2024. All rights reserved.