2017-07-08 82 views
0

我有父(Property)和子(Credit)之間的關係,我的Hibernate配置對我很好,只要我不嘗試通過家長(財產)通過Cascade保存新的子女(信用)。JPA/Hibernate @OneToMany在JoinTable上帶有可選Child - 保存父節點影響JoinTable上的多個插入

Hibernate的配置:

@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內容 enter image description here 任何提示都非常受歡迎。

編輯: 附加信息。調用存儲庫之後。保存列表仍被視爲髒(髒=真)。它不應該是假的,當它剛剛被保存時,因爲在控制檯中,我可以看到它只是發送SQL到數據庫插入。在調試時,如果我給列表的髒= false,那麼第二次插入將不會觸發更多,一切都很好。爲什麼在將插入數據發送到數據庫之後沒有將其設置爲false。它不應該更髒,或者我認爲是錯誤的?

SOLUTION

由於crizzis建議我在Property類中刪除@JoinTable從集合屬性creditOptions並添加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; 

回答

1

的關聯條目被插入到兩次的連接表的原因是,你實際上已經宣佈兩個獨立關聯(它恰好使用相同的連接表)只有一個。

建模時在JPA雙向關聯,側之一必須是所屬側,和另一側必須是逆側。反面是其關聯映射聲明mappedBy屬性的那一面。

解決方法是簡單地將關聯的一邊聲明爲反面,並從該邊刪除@JoinTable註釋。否則,JPA會將每一方視爲單獨的單向關聯。

請注意,對於所有JPA都知道,您可能出於某種原因想要有兩個單獨的單向關聯(例如,使用不同的連接表)。 JPA根本不會自動知道您的意圖 - 您需要mappedBy屬性來使其清晰。

+0

嘿,真棒,我剛從'NotActiveCredit'類中刪除了JoinTable,它已經可以工作了。 'mappedBy'不是必需的。 –

+0

這仍然會被視爲一個單獨的映射。唯一的區別是'@ ManyToOne'默認使用連接列,因此您可能會在'NONACTIVECREDIT'表中看到額外的'PROPERTY_ID'列。更重要的是,'Property.creditOptions'和'NotActiveCredit.property'不會被JPA – crizzis

+0

保持同步表'not_active_credit'只創建一個'property_id',但我會仔細看看同步。順便說一句。 @ManyToOne註釋沒有'mappedBy',你會在哪裏建議添加它?謝謝。 –