【问题标题】:JPA / Hibernate @OneToMany with optional Child on JoinTable - Save parent affect multiple Inserts on JoinTableJPA / Hibernate @OneToMany 与 JoinTable 上的可选子项 - 保存父项会影响 JoinTable 上的多个插入
【发布时间】:2017-07-08 13:32:46
【问题描述】:

我在父(属性)和子(信用)之间有关系,只要我不尝试通过父(属性)级联保存新的子(信用),我的休眠配置就可以正常工作。

休眠配置:

@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

我的对象的内容 任何提示都非常受欢迎。

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

解决方案

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

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

NotActiveCredit类的另一边是这样的:

@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;

【问题讨论】:

    标签: hibernate jpa spring-data-jpa one-to-many jointable


    【解决方案1】:

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

    在 JPA 中对双向关联进行建模时,其中一方必须是拥有方,另一方必须是反方。反面是关联映射声明mappedBy 属性的那一面。

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

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

    【讨论】:

    • 嘿,太棒了,我刚刚从 NotActiveCredit 类中删除了 JoinTable,它已经可以工作了。 mappedBy 没有必要。
    • 这仍将被视为单独的映射。唯一的区别是@ManyToOne 默认使用连接列,因此您可能会在NONACTIVECREDIT 表中看到一个额外的PROPERTY_ID 列。此外,Property.creditOptionsNotActiveCredit.property不会由 JPA 保持同步
    • not_active_credit 仅创建了一个 property_id,但我将仔细查看同步。顺便提一句。 @ManyToOne 注释没有mappedBy,你建议在哪里添加它?谢谢。
    • 嗯,单个property_id 已经是多余的,因为所有关于PropertyNotActiveCredit 之间关联的信息都应该存储在连接表中。至于mappedBy,你应该将它添加到@OneToMany,并将@JoinTable注解移动到NotActiveCredit。这样,NotActiveCredit 将成为拥有方
    • 嘿,Crizzis,你是绝对正确的。 not_active_credit 表上有多余的 property_id。我按照你的建议做了,首先它仍然有效,其次,多余的property_id 不见了。很好,您是 Hibernate 开发人员吗? :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    • 2018-10-14
    • 1970-01-01
    相关资源
    最近更新 更多