Hibernate:仅将 ManyToOne 映射到具有特定列值的外键实体

Hibernate: Mapping ManyToOne only to foreign key entities with particular column value

我觉得这有点奇怪,我不确定这是否可能,但我的要求如下:

我有两个实体:DatasetOrganizationDataset 实体具有到 Organization 实体的多对一映射。组织有两种类型:CustomerPartner。业务需求是只有 Partner 类型的组织可以有数据集。那么,有没有什么方法可以将 Dataset 实体映射到 Organization,使得 Dataset 中的所有外键仅包含类型为 [=17= 的 Organization 实体的 ID ]?

组织实体定义如下:

@Entity
@Table(name = "organization")
public class Organization {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;    
    private String type;
    private String name;
    private String address;
    private String status;
    private String subtype;
    
    @Column(name = "created_date")
    @Temporal(value =  TemporalType.TIMESTAMP)
    private Date createdDate;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "organization_id", insertable = false, updatable = false)
    private List<User> users;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns({ @JoinColumn(name = "subtype", referencedColumnName = "name", insertable = false, updatable = false),
            @JoinColumn(name = "type", referencedColumnName = "organization_type", insertable = false, updatable = false) })
    private OrganizationSubType organizationSubType;

    // Getters and setters
.
.
.

此处,type 列将包含 PARTNERCUSTOMER

这是我目前正在设计的数据集实体:

@Entity
@Table(name="datasets")
public class Dataset {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    @Column(name="partner_id", nullable = false)
    private long partnerId;
    
    @Column(nullable = false, unique = true)
    private String name;
    
    private String description;
    
    @Column(nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;
    
    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    private DatasetStatus datasetStatus;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="partner_id", referencedColumnName="id", insertable = false, updatable = false)
    private Organization partner;
    
    //Getters and setters

那么,有什么方法可以对映射设置约束,使得在持久化新数据集实体时,组织 ID 始终属于类型为合作伙伴而非客户的实体?或者我是否必须将客户和合作伙伴组织作为单独的实体分开?

有几个选项可以实现它:

  1. 通过在持久化之前构建新的 Dataset 对象时在业务层添加验证。此类验证将检查给定 Organisation 是否可以与基于组织类型创建的 Dataset 实体相关联。

  2. 通过使用 Bean 验证 API 并定义自定义约束验证器。实体上的每次更改都会调用此类验证器。

将hibernate-validator 添加到类路径

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>5.3.4.Final</version>
</dependency>

为新验证定义注解

@Constraint(validatedBy = PartnerOrganisationValidator.class)
@Target(FIELD)
@Retention(RUNTIME)
@Documented
public @interface PartnerOrganisation {
    String message() default "Organisation should be of type PARTNER";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

定义约束验证器

public class PartnerOrganisationValidator implements ContraintValidator<PartnerOrganisation, Organisation> {
    @Override
    public boolean isValid(Organisation organisation, ConstraintValidatorContext constraintValidatorContext) {
        return organisation == null || "PARTNER".equals(organisation.type);
    }
}

通过将验证器的完全限定名称添加到 META-INF/services/javax.validation.ConstraintValidator 文件,在休眠验证器中注册验证器。 最后一步是在您的实体中使用验证器:

    @PartnerOrganisation
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="partner_id", referencedColumnName="id", insertable = false, updatable = false)
    private Organization partner;
  1. 如果您使用的数据库支持它,则使用 SQL CHECK CONSTRAINT - 就像

    中的示例一样
  2. 通过将合作伙伴组织和客户组织建模为单独的实体 类。

由于合作伙伴和客户的组织结构与选项 4 中的方法相同。对于这种情况似乎过于复杂。我建议选择选项 1。