簡體   English   中英

為什么 Spring JPA 雙向 OneToMany 和 ManyToOne 不更新外鍵列?

[英]Why Spring JPA Bidirectional OneToMany and ManyToOne is not updating the foreign key column?

Hi i am learning Spring JPA using OneToMany and ManyToOne bidirectional relationship, in some example i see OneToMany and ManyToOne relationship when i written in two side, the JPA add a new column as the foreign key column and insert the key value from the Parent table. 但是當我嘗試我的時,該列始終為空白。 這是我的代碼的樣子:

這是我的 Account.java model:

@Entity
@Table(name = "msAccount")
public class Account {

    @Id
    @NotBlank(message = "Not Blank")
    @Size(min = 0, max = 20)
    public String accountId;

    @NotBlank(message = "Not Blank")
    public String accountName;

    @NotBlank(message = "Not Blank")
    @Email(message = "Should be the right email")
    public String accountEmail;

    @NotBlank(message = "Not Blank")
    @Size(min = 5, message = "Minimal 5 char")
    public String accountAddress;

    @NotBlank(message = "Not Blank")
    public String town;

    @NotBlank(message = "Not Blank")
    public String npwp;

    @NotBlank(message = "Not Blank")
    public String phoneNumber;

    public String fax;

    public String remarks;

    @NotNull
    public Date entryTime;

    @NotNull
    public Boolean active;

    @OneToMany(mappedBy="account", cascade = CascadeType.ALL, orphanRemoval = true)
    public List<Dealer> dealer;

//getter setter skipped

}

這是我的經銷商。java model:

@Entity
@Table(name = "msDealer")
public class Dealer {

    @Id
    @NotBlank(message = "Tidak Boleh Kosong")
    @Size(min = 0, max = 20)
    public String dealerId;

    @NotBlank(message = "Tidak Boleh Kosong")
    public String dealerName;

    @NotBlank(message = "Tidak Boleh Kosong")
    @Email(message = "Masukkan Email yang bener")
    public String dealerEmail;

    @NotBlank(message = "Tidak Boleh Kosong")
    @Size(min = 5, message = "Minimal 5 karakter")
    public String dealerAddress;

    @ManyToOne(fetch = FetchType.LAZY)
    public Account account;

//getter setter skipped

}

這是我的存儲庫:

@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {

}

這是我的服務:

@Service
public class AccountService {

    @Autowired
    private AccountRepository accountRepository;

    public Account save(Account account) {
        return accountRepository.save(account);
    }

}

這是我的 controller:

@RestController
@RequestMapping("/api/account")
public class AccountController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final int ROW_PER_PAGE = 10;

    @Autowired
    private AccountService accountService;

    @PostMapping("/new")
    public ResponseEntity<Account> addAccount(@Valid @RequestBody Account account) {
        try {
            Account newAccount = accountService.save(account);
            return ResponseEntity.created(new URI("/api/account/" + newAccount.getAccountId()))
                    .body(account);
        } catch(Exception ex) {
            logger.error(ex.getMessage());
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
    }

}

然后我將 JSON 發布到我的保存端點:

{
  "accountId": "USA001",
  "accountName": "string",
  "accountEmail": "string",
  "accountAddress": "string",
  "town": "string",
  "npwp": "string",
  "phoneNumber": "string",
  "fax": "string",
  "remarks": "string",
  "entryTime": "2020-04-07T15:01:29.404Z",
  "active": true,
  "dealer": [
    {
      "dealerId": "MMO001",
      "dealerName": "string",
      "dealerEmail": "string",
      "dealerAddress": "string"
    }
  ]
}

當我保存它時,出現在我的終端中的 hibernate 看起來將查詢插入到那 2 個表中,但是當我檢查我的數據庫表(即 postgresql)時,我發現有一個字段“account_account_id”是 null,我錯過了什么這里?

我希望 Hibernate 像這樣運行 sql:

insert into account (account_id, account_name, ...etc)
values ('USA001', 1)

insert into dealer (account_account_id, dealer_name, dealer_id, ...etc)
values ('USA001', 'New dealer 1', 'MMO001')

經過一番嘗試,這是我更新的 Model:

我的 Account.java 我刪除cascade = CascadeType.ALL, orphanRemoval = true

@Entity
@Table(name = "msAccount")
public class Account {

    @Id
    @NotBlank(message = "Tidak Boleh Kosong")
    @Size(min = 0, max = 20)
    public String accountId;

    @NotBlank(message = "Tidak Boleh Kosong")
    public String accountName;

    @NotBlank(message = "Tidak Boleh Kosong")
    @Email(message = "Masukkan Email yang bener")
    public String accountEmail;

    @NotBlank(message = "Tidak Boleh Kosong")
    @Size(min = 5, message = "Minimal 5 karakter")
    public String accountAddress;

    @NotBlank(message = "Tidak Boleh Kosong")
    public String town;

    @NotBlank(message = "Tidak Boleh Kosong")
    public String npwp;

    @NotBlank(message = "Tidak Boleh Kosong")
    public String phoneNumber;

    public String fax;

    public String remarks;

    @NotNull
    public Date entryTime;

    @NotNull
    public Boolean active;

    @OneToMany(mappedBy="account")
    // @JoinColumn(name = "accountId")
    public List<Dealer> dealer;

//getter setter skipped

}

這是我的經銷商。java。 添加了@JoinColumn:

@Entity
@Table(name = "msDealer")
public class Dealer {

    @Id
    @NotBlank(message = "Tidak Boleh Kosong")
    @Size(min = 0, max = 20)
    public String dealerId;

    @NotBlank(message = "Tidak Boleh Kosong")
    public String dealerName;

    @NotBlank(message = "Tidak Boleh Kosong")
    @Email(message = "Masukkan Email yang bener")
    public String dealerEmail;

    @NotBlank(message = "Tidak Boleh Kosong")
    @Size(min = 5, message = "Minimal 5 karakter")
    public String dealerAddress;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "account_id")
    public Account account;

//getter setter skipped

}

現在錯誤越來越奇怪,當我保存 JSON 數據時出現此錯誤

> "Unable to find com.api.b2b.Model.Dealer with id MMO001; nested
> exception is javax.persistence.EntityNotFoundException: Unable to find
> com.api.b2b.Model.Dealer with id MMO001"

在某些教程中它有效,但我的不是,我做錯了什么?

這是我的 github 回購: https://github.com/Fly-Away/LearningSpring

您缺少子方面的@JoinColumn

@Entity
@Table(name = "ms_dealer")
public class Dealer {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "account_account_id")
    public Account account;

    // other fields

}

您在父端使用了mappedBy ,但在子端沒有映射。 您需要指出, Dealer是關系所有者 - 它具有外鍵。

編輯:如果您保留(不合並) Account實體及其子實體,則不應傳遞子實體的 ID。 (實際上在持久化上傳遞任何 id 都是代碼異味,很可能是性能殺手。)使用的 json 應該如下所示:

{
  "accountName": "string",
  "accountEmail": "string",
  "accountAddress": "string",
  "town": "string",
  "npwp": "string",
  "phoneNumber": "string",
  "fax": "string",
  "remarks": "string",
  "entryTime": "2020-04-07T15:01:29.404Z",
  "active": true,
  "dealer": [
    {
      "dealerName": "string",
      "dealerEmail": "string",
      "dealerAddress": "string"
    }
  ]
}

在保存雙方同步之前,可能還需要:

account.getDealer().forEach(d -> d.setAccount(account));

編輯:

Author編輯必須級聯到子級:

@OneToMany(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval = true)
public List<Dealer> dealer;

您還可以在ActionList<Dealer>上添加@JsonIgnore以避免序列化到 json 時出現堆棧溢出。

要在雙向關系中保存與父級的子級,在子級實體中設置父級也要同步雙方。

此處在dealer對象中設置account參考

public Account save(Account account) {
    for (Dealer dealer: account.getDealer()) {
        dealer.setAccount(account);
    }
    return accountRepository.save(account);
}

更新:

但是,如果您想使用單向關系,請刪除Dealer實體中的Account關系。 刪除這部分

@ManyToOne(fetch = FetchType.LAZY)
public Account account;

然后更新Account表中的關系。

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "account_id")
public List<Dealer> dealer;

這里我們移除了mappedBy ,因為目前我們移除了Dealer端的映射,並添加@JoinColumn來定義我們使用哪一列作為賬戶參考。

如果您在兩個實體(此處為AccountDealer )之間存在雙向關系,則您必須決定哪一方是所述關系的所有者。 默認情況下,一側是所有者,它導致在修改列表時更新的聯接表。

由於您定義了mappedBy屬性( @OneToMany(mappedBy = "account") ),因此Many -side 是關系的所有者。 這意味着msDealer表中的account列將保存Account的外鍵,然后 Join-Table 將不再使用。 Join-Table 可能是在將mappedBy定義添加到注釋之前初始化數據庫的遺留物。

您擁有的選項:

  1. Dealer保持所有者身份,不要使用 Join-Table。 如果您想觀察數據庫中的副作用,請查看列msDealer.account
  2. 使用@JoinTable注釋來強制使用這樣的表

正如您所說的您正在學習,我想給您一個詳細的答案,以便您理解。 您在這里缺少的是@JoinColumn

@JoinColumn可用於關系的雙方。 這里的重點是物理信息重復(列名)以及未優化的 SQL 查詢,這將產生一些額外的UPDATE語句

根據文件

由於多對一(幾乎)始終是 JPA 規范中雙向關系的所有者,一對多關聯由@OneToMany(mappedBy=...)

通過基本代碼示例理解

@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
} 

Troop通過部隊屬性與Soldier具有雙向的一對多關系。 您不必(不得)在mappedBy端定義任何物理映射。

對於 map 雙向一對多,以一對多作為擁有方,您必須刪除mappedBy元素並將多對一@JoinColumn設置為可insertableupdatable為 false。 此解決方案未優化,會產生一些額外的UPDATE語句。

@Entity
public class Troop {
    @OneToMany
    @JoinColumn(name="troop_fk") //we need to duplicate the physical information
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk", insertable=false, updatable=false)
    public Troop getTroop() {
    ...
}

如果您對給出的解釋有任何進一步的問題,請在下方評論。 :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM