簡體   English   中英

使用外鍵關系編排Spring Boot CrudRepositories

[英]Orchestrating Spring Boot CrudRepositories with foreign key relationships

我正在編寫一個Spring Boot應用程序,它將使用Hibernate / JPA在應用程序和MySQL數據庫之間保持不變。

這里我們有以下JPA實體:

@MappedSuperclass
public abstract class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonIgnore
    private Long id;

    @Type(type="uuid-binary")
    private UUID refId;
}

@Entity(name = "contacts")
@AttributeOverrides({
        @AttributeOverride(name = "id", column=@Column(name="contact_id")),
        @AttributeOverride(name = "refId", column=@Column(name="contact_ref_id"))
})
public class Contact extends BaseEntity {
    @Column(name = "contact_given_name")
    private String givenName;

    @Column(name = "contact_surname")
    private String surname;

    @Column(name = "contact_phone_number")
    private String phone;
}

@Entity(name = "assets")
@AttributeOverrides({
        @AttributeOverride(name = "id", column=@Column(name="asset_id")),
        @AttributeOverride(name = "refId", column=@Column(name="asset_ref_id"))
})
public class Asset extends BaseEntity {
    @Column(name = "asset_location")
    private String location;
}

@Entity(name = "accounts")
@AttributeOverrides({
    @AttributeOverride(name = "id", column=@Column(name="account_id")),
    @AttributeOverride(name = "refId", column=@Column(name="account_ref_id"))
})
public class Account extends BaseEntity {
    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "contact_id", referencedColumnName = "contact_id")
    private Contact contact;

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "asset_id", referencedColumnName = "asset_id")
    private Asset asset;

    @Column(name = "account_code")
    private String code;
}

以及@RestController ,其中一個Account實例將被POST(要創建):

public interface AccountRepository extends CrudRepository<Account, Long> {
    @Query("FROM accounts where account_code = :accountCode")
    public Account findByCode(@Param("accountCode") String accountCode);
}

@RestController
@RequestMapping(value = "/accounts")
public class AccountController {
    @Autowired
    private AccountRepository accountRepository;

    @RequestMapping(method = RequestMethod.POST)
    public void createNewAccount(@RequestBody Account account) {
        // Do some stuff maybe

        accountRepository.save(account);
    }
}

所以這里的想法是“Account JSON”將被發送到這個控制器,在那里它將被反序列化為一個Account實例,並且(某種程度上)持久化到支持MySQL。 我關心的是: Account是其他幾個實體的組合(通過外鍵)。 我需要:

  • 為這些實體中的每一個創建CrudRepository impl,然后編排對這些存儲庫的save(...)調用,以便在“外部” Account實體之前首先保存“內部權限”? 要么
  • 我只是保存Account實體(通過AccountRepository.save(account) )和Hibernate / JPA自動負責為我創建所有內部/依賴實體嗎?

在任一場景中,代碼/解決方案會是什么樣子? 如果它是DB中的自動遞增PK,我們如何指定BaseEntity#id值?

這取決於您的設計和特定用例,以及您希望保持的靈活程度。 兩種方式都在實踐中使用。

在大多數CRUD情況下,您寧願保存帳戶並讓Hibernate保存整個圖表(第二個選項)。 在這里你通常有另一個你沒有提到的案例,它是圖表的更新,你可能會以同樣的方式做,實際上Spring的存儲庫save方法是這樣做的:如果實體是一個新的(瞬態的) ,它堅持下去,否則它合並它。

您需要做的就是告訴Hibernate將所需的實體生命周期操作從Account級聯到相關實體:

@Entity
...
public class Account extends ... {
    @OneToOne(..., cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    ...
    private Contact contact;

    @OneToOne(..., cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    ...
    private Asset asset;

    ...
}

但是,在合並操作的情況下,您需要支付從db重新加載對象圖的懲罰,但如果您希望自動完成所有操作,Hibernate除了將其與當前狀態進行比較之外沒有其他方法來檢查實際發生了什么變化。 D b。

總是應用級聯操作,因此如果您需要更大的靈活性,您顯然必須手動處理事務。 在這種情況下,您將省略級聯選項(這是您當前的代碼),並按照不破壞任何完整性約束的順序手動保存和更新對象圖的各個部分。

雖然涉及一些樣板代碼,但手動方法可以在更復雜或性能要求更高的情況下提供靈活性,例如當您不想加載或重新初始化分離圖的部分時,您知道它們在某些上下文中未被更改。你保存它。

例如,假設有一個案例,其中有單獨的Web服務方法來更新帳戶,聯系人和資產。 在帳戶方法的情況下,使用級聯選項,您需要加載整個帳戶圖表以合並帳戶本身的更改,盡管聯系人和資產不會更改(或者更糟糕的是,取決於您的操作方式,您可能如果您只使用帳戶中包含的分離實例,此處還會恢復其他人在其專用方法中對其進行的更改。

關於自動生成的ID,您不必自己指定它們,只需從保存的實體中獲取它們(Hibernate將其設置在那里)。 如果您計划之后使用更新的實體,則獲取存儲庫的save方法的結果非常重要,因為merge操作始終返回傳入實例的合並副本,並且更新中是否存在任何新保留的關聯實體實例分離圖,它們的ID將在副本中設置,原始實例不會被修改。

暫無
暫無

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

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