JPA/Hibernate @OneToMany 在 JoinTable 上带有可选 Child - 保存 parent 影响 JoinTable 上的多个插入

JPA / Hibernate @OneToMany with optional Child on JoinTable - Save parent affect multiple Inserts on JoinTable

我在 Parent (属性) 和 Children (Credit) 之间有关系,我的 Hibernate 配置对我来说工作正常,只要我不尝试保存Cascade 通过 Parent (属性) 的新 Child(信用)。

休眠配置:

@Entity
public class Property implements Ownable {

    ...

        @Valid 
        // ALSO TRIED WITH CascadeType.ALL !!!
        @OneToMany(fetch = LAZY, cascade = {PERSIST, MERGE, REMOVE}, orphanRemoval = true)
        @JoinTable(
                name = "PROPERTY_CREDIT_OPTIONS",
                joinColumns = @JoinColumn(name="PROPERTY_ID", nullable = false), 
                inverseJoinColumns = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
        )
        List<NotActiveCredit> creditOptions = new ArrayList<>();

    ...

}

@Entity
public class NotActiveCredit extends Credit {

    @ManyToOne
    @JoinTable(
        name                = "PROPERTY_CREDIT_OPTIONS",
        inverseJoinColumns  = @JoinColumn(name="PROPERTY_ID", nullable = false), 
        joinColumns         = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
    )
    @NotNull @NonNull 
    Property    property;
}

控制器调用:

@PostMapping("/save")
public String save (
        @ModelAttribute("property") @Valid Property formProperty, 
        BindingResult errors, 
        ModelMap modelMap
) {
    if (!errors.hasErrors()) {

        // formProperty has new Credit in the creditOptions List
        // SAVE ONLY DELEGATS TO SPRING DATA REPOSITORY ...
        final Property savedProperty = propertyService.save(formProperty);
        ...     

    } else {
        // BLABLA
    }
}

日志看起来像:

Enter InvestmentController.save( ... )
Enter PropertyServiceImpl.save( ... ) 

insert into not_active_credit (id, interest_rate_nominal_in_percent, name_of_institution, redemption_at_begin_in_percent, special_redemption_each_year_in_percent) values (default, ?, ?, ?, ?)
binding parameter [1] as [NUMERIC] - [null]
binding parameter [2] as [VARCHAR] - [test]
binding parameter [3] as [NUMERIC] - [null]
binding parameter [4] as [NUMERIC] - [null]

insert into property_credit_options (property_id, credit_id) values (?, ?)
binding parameter [1] as [BIGINT] - [1]
binding parameter [2] as [BIGINT] - [1]

Exit PropertyServiceImpl.save Returns Property

// THEN WHEN IT CLOSE THE TRANSACTION IT WANTS TO INSERT JOIN TABLE AGAIN!
insert into property_credit_options (property_id, credit_id) values (?, ?)
binding parameter [1] as [BIGINT] - [1]
binding parameter [2] as [BIGINT] - [1]

SQL Error: -104, SQLState: 23505
integrity constraint violation: unique constraint or index violation; SYS_PK_10222 table: PROPERTY_CREDIT_OPTIONS

我的Object`s内容 任何提示都非常受欢迎。

编辑: 附加信息。在调用 Repository.Save 之后,列表仍然被认为是脏的 (dirty=true)。当它刚刚被保存时它不应该是假的,因为在控制台中我可以看到它只是将 SQL 发送到数据库进行插入。在调试时,如果我给列表 dirty=false 那么第二次 Insert 将不会被触发,一切都很好。为什么在向 DB 发送插入后不将其设置为 false。应该不会更脏吧,还是我想错了?

解决方案

正如 crizzis 所建议的,我从 属性 creditOptions class Property 中删除了 @JoinTable 并添加了 mappedBy='property'。该集合现在可以从 parent 更新。

@Valid 
@OneToMany(mappedBy="property", fetch = LAZY, cascade = ALL, orphanRemoval = true)
Collection<NotActiveCredit>     creditOptions = new ArrayList<>();  

另一边classNotActiveCredit是这样的:

@ManyToOne
@JoinTable(
        name                = "PROPERTY_CREDIT_OPTIONS",
        inverseJoinColumns  = @JoinColumn(name="PROPERTY_ID", nullable = false), 
        joinColumns         = @JoinColumn(name = "CREDIT_ID", nullable = false, unique = true)
)
@NotNull  
Property    property;

关联条目被两次插入联接的原因 table 是您实际上声明了 两个单独的 关联(恰好使用same join table) 而不是只有一个。

在 JPA 中建模双向关联时,其中一端必须是拥有端,另一端必须是反向端[=24] =].相反的一面是其关联映射声明 mappedBy 属性的一面。

解决方案很简单,将关联的一侧声明为反面,并从该侧删除 @JoinTable 注释。否则 JPA 会将每一方视为单独的单向关联。

请注意,就 JPA 所知,您可能出于某种原因希望拥有两个单独的单向关联(例如,使用不同的连接 table)。 JPA 根本不会自动知道您的意图 - 您需要 mappedBy 属性来明确它们。