java - 使用外键关系编排 Spring Boot CrudRepositories

标签 java hibernate jpa spring-boot spring-data-jpa

我正在编写一个 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 实例将被发布(待创建):

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”将被发送到这个 Controller ,在那里它将被反序列化为一个 Account 实例,并(以某种方式)持久保存到支持 MySQL。我担心的是:Account 是几个其他实体的组合(通过外键)。我需要:

  • 要么为这些实体中的每一个创建 CrudRepository 实现,然后编排对这些存储库的 save(...) 调用,以便保存“内部实体”首先在“外部”Account 实体之前?或
  • 我是否只保存 Account 实体(通过 AccountRepository.save(account)),Hibernate/JPA 会自动为我创建所有内部/依赖实体?

在这两种情况下,代码/解决方案会是什么样子?当 BaseEntity#id 是数据库中的自动递增 PK 时,我们如何指定值?

最佳答案

这取决于您的设计和具体用例,以及您希望保持何种程度的灵 active 。这两种方式在实践中都有使用。

在大多数 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;

    ...
}

但是,在合并操作的情况下,您要付出从数据库重新加载对象图的代价,但是如果您希望自动完成所有操作,Hibernate 除了将它与当前的进行比较之外,没有其他方法来检查实际更改的内容数据库中的状态。

级联操作总是应用,所以如果你想要更多的灵 active ,你显然必须手动处理事情。在这种情况下,您将省略级联选项(这是您当前的代码),并按照不破坏任何完整性约束的顺序手动保存和更新对象图的各个部分。

虽然涉及一些样板代码,但手动方法可以让您在更复杂或对性能要求更高的情况下具有灵 active ,例如当您不想加载或重新初始化分离图的部分时,您知道它们在您保存它的一些上下文。

例如,我们假设有单独的 Web 服务方法用于更新帐户、联系人和 Assets 的情况。在帐户方法的情况下,使用级联选项,您需要加载整个帐户图表才能合并帐户本身的更改,尽管联系人和 Assets 没有更改(或者更糟,取决于您的操作方式,您可能如果您只使用帐户中包含的分离实例,则在此期间恢复其他人在其专用方法中对它们所做的更改。

关于自动生成的 id,你不必自己指定它们,只需从保存的实体中获取它们(Hibernate 会在那里设置)。如果您打算以后使用更新后的实体,那么获取存储库的 save 方法的结果很重要,因为 merge 操作总是返回传入实例的合并副本,如果更新后的分离图中有任何新持久化的关联实体实例,它们的 id 将在副本中设置,并且不会修改原始实例。

关于java - 使用外键关系编排 Spring Boot CrudRepositories,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43408067/

相关文章:

java - 为什么我的 PreferenceScreen 会强制关闭?

java - 如何获取另一个应用程序的图标?

Java正则表达式从日志文件中获取子度量

java - 引用条件查询中较早的别名字段

java - Hibernate 和 JPA 中的 @Entity 有什么区别

Java 程序多次出现在系统托盘中

hibernate - grails withSession 和当前的 hibernate session

java - Spring data Jpa 与 Spring Jdbc 用于查找表

java - 公式延迟加载不起作用

spring - JPA @Entity 中的 Bean 注入(inject)