刷新单向实体时不违反空约束

Posted

技术标签:

【中文标题】刷新单向实体时不违反空约束【英文标题】:Not null constraint violation when flushing unidirectional entities 【发布时间】:2020-11-06 12:23:51 【问题描述】:

我正在使用 @OneToMany@ManyToOne 注释进行单向和双向映射,但在持久化实体并将它们刷新到数据库时,我无法打破单向映射的障碍。

所以,两个表delivery_company 可能有很多delivery

SQL(甲骨文):

CREATE TABLE delivery (
    delivery_id          NUMBER(6) NOT NULL,
    price                NUMBER(5, 2) NOT NULL,
    delivery_time        DATE NOT NULL,
    delivery_company_id  NUMBER(2) NOT NULL
);

ALTER TABLE delivery ADD CONSTRAINT delivery_pk PRIMARY KEY ( delivery_id );

CREATE TABLE delivery_company (
    delivery_company_id    NUMBER(2) NOT NULL,
    delivery_company_name  VARCHAR2(15 CHAR) NOT NULL
);

ALTER TABLE delivery_company ADD CONSTRAINT delivery_company_pk PRIMARY KEY ( delivery_company_id );

ALTER TABLE delivery
    ADD CONSTRAINT delivery_delivery_company_fk FOREIGN KEY ( delivery_company_id )
        REFERENCES delivery_company ( delivery_company_id );

单向映射:

@Entity
@Table(name = "Delivery")
class DeliveryUniDirectional

    @Id
    @SequenceGenerator(name = "delivery_id_sequence", sequenceName = "delivery_id_sequence", allocationSize = 1)
    @GeneratedValue(generator = "delivery_id_sequence", strategy = GenerationType.SEQUENCE)
    @Column(name = "delivery_id")
    public Long deliveryId;

    public BigDecimal price;

    @Temporal(TemporalType.DATE)
    public Date deliveryTime;

// setters, getters


@Entity
@Table(name = "delivery_company")
class DeliveryCompanyUniDirectional 

    @Id
    @Column(name = "delivery_company_id")
    @SequenceGenerator(name = "delivery_company_id_sequence", sequenceName = "delivery_company_id_sequence", allocationSize = 1)
    @GeneratedValue(generator = "delivery_company_id_sequence", strategy = GenerationType.SEQUENCE)
    private Long deliveryCompanyId;

    @Column(unique = true)
    private String deliveryCompanyName;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "delivery_id", nullable = false, insertable = false, updatable = false)
    private List<DeliveryUniDirectional> deliveries = new LinkedList<>();

// setters getters


当我运行@DataJpaTest 测试时:

    @Test
    public void insertDeliveryUniDirectional()
    
        DeliveryCompanyUniDirectional deliveryCompany = new DeliveryCompanyUniDirectional();
        deliveryCompany.setDeliveryCompanyName("aa");

        DeliveryUniDirectional delivery = new DeliveryUniDirectional();
        delivery.setPrice(BigDecimal.ONE);
        delivery.setDeliveryTime(new Date());

        deliveryCompany.getDeliveries().add(delivery);

        entityManager.persist(deliveryCompany);
        entityManager.flush();
    

我收到

javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute batch ...
// 
Caused by: java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("TESTUSER"."DELIVERY"."DELIVERY_COMPANY_ID")

entityManager.flush();.

我尝试在 DeliveryCompanyUniDirectional 中使用 @JoinColumn 而不可插入和更新,但在这种情况下 hibernate 抱怨:

Error creating bean with name 'entityManagerFactory' defined in class path resource ...
// ...
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: wieczorek.jakub.shop.business.spring.model.domain.DeliveryUniDirectional column: delivery_id (should be mapped with insert="false" update="false")

delivery 表中外键的NOT NULL 约束肯定有问题。当我尝试使用双向映射时,持久化和刷新效果非常好,但我想使用单向实现同样的效果。

感谢阅读

【问题讨论】:

【参考方案1】:

你的@JoinColumn 应该是delivery_company_id,因为它是你的外键

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name="delivery_company_id", referencedColumnName="delivery_company_id", nullable = false)
private List<DeliveryUniDirectional> deliveries = new LinkedList<>();

【讨论】:

我当时正准备不同意 Eklavya 的观点,因为此时 delivery_company_id 是您所在的表中的一列。但那是因为我略读并意识到你没有遵循我认为的约定:任何表中的 id 列都只是命名为 id,只有外键命名为 _id。当您进行 OneToMany 注释时,JoinColumn 始终是另一个表中指向我的列。 @Zag 见post中建表sql,更新更清晰 感谢@Eklayva 的工作解决方案(我只需要添加nullable = false,因为非空约束)。感谢@Zag 的有用评论。

以上是关于刷新单向实体时不违反空约束的主要内容,如果未能解决你的问题,请参考以下文章

在持久化实体之前检查是不是违反约束

当使用违反唯一约束的实体调用 persist() 时没有持久性异常

如何避免使用 JPA 在实体关系中违反外键约束

填充行的其余部分/避免违反空约束

如何在不违反主键约束的情况下插入具有循环引用的实体框架

为啥这违反了类型参数“TUser”的约束?