英文:
Hibernate: Mapping ManyToOne only to foreign key entities with particular column value
问题
我认为这有点奇怪,我不确定这是否可能,但我的要求如下:
我有两个实体:Dataset
和 Organization
。Dataset
实体与 Organization
实体之间有多对一的映射关系。组织分为两种类型:Customer
和 Partner
。业务要求是,只有属于 Partner
类型的组织才能拥有数据集。那么,有没有办法将 Dataset
实体映射到 Organization
,以便 Dataset
中的所有外键只包含属于 Partner
类型的 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
列将包含 PARTNER
或 CUSTOMER
。
这是我当前设计的 Dataset 实体:
@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
// ...
}
那么,有没有办法在映射上设置约束,使得当持久化新的 Dataset 实体时,组织的 ID 始终属于 Partner 类型的实体,而不是 Customer?或者我必须将 Customer 和 Partner 组织分别作为单独的实体?
英文:
I think this is a bit weird, I am not sure if this is even possible, but my requirement is as follows:
I have two entities: Dataset
and Organization
. The Dataset
entity has a many-to-one mapping to the Organization
entity. Organizations are of two types: Customer
and Partner
. The business requirement is that only Partner
type organizations can have datasets. So, is there any way to map the Dataset
entity to Organization
such that all foreign keys in Dataset
only contain ids of Organization
entities that are of type Partner
?
The organization entity is defined as follows:
@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
.
.
.
Here, the type
column will contain either PARTNER
or CUSTOMER
.
And here's the Dataset entity I am currently designing:
@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
So, is there any way to set a constraint on the mapping in such a way that when a new Dataset entity is persisted, the organization id always belongs to an entity of type partner and not customer? Or do I have to separate the customer and partner organizations as separate entities?
答案1
得分: 1
有几种方法可以实现这个目标:
-
通过在构建新的
Dataset
对象并将其持久化之前,在业务层添加验证。这种验证将检查是否可以基于组织类型将给定的Organisation
关联到创建的Dataset
实体中。 -
通过利用 Bean Validation 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 ConstraintValidator<PartnerOrganisation, Organisation> {
@Override
public boolean isValid(Organisation organisation, ConstraintValidatorContext constraintValidatorContext) {
return organisation == null || "PARTNER".equals(organisation.type);
}
}
通过将您的验证器的完全限定名添加到 META-INF/services/javax.validation.ConstraintValidator
文件中,在 hibernate 验证器中注册验证器。最后一步是在实体中使用验证器:
@PartnerOrganisation
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="partner_id", referencedColumnName="id", insertable = false, updatable = false)
private Organization partner;
-
如果您使用的数据库支持,可以通过使用
SQL CHECK CONSTRAINT
来实现,就像 https://stackoverflow.com/a/55379219/14231619 中的示例一样。 -
将 PARTNER 组织和 CUSTOMER 组织建模为单独的实体类。由于 PARTNER 和 CUSTOMER 的组织结构相同,选项 4 的方法对于这种情况似乎过于复杂。我建议选择选项 1。
英文:
There are several option how you can achieve it:
-
By adding validation at business layer in the place when you build new
Dataset
object prior to persisting it. Such validation would check if givenOrganisation
can be associated with createdDataset
entity based on organisation type. -
By utilizing Bean Validation API & defining custom constraint validator. Such validator will be invoked by each change on the entity.
Add hibernate-validator to the classpath
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.4.Final</version>
</dependency>
Define annotation for new validation
@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 { };
}
Define contraint validator
public class PartnerOrganisationValidator implements ContraintValidator<PartnerOrganisation, Organisation> {
@Override
public boolean isValid(Organisation organisation, ConstraintValidatorContext constraintValidatorContext) {
return organisation == null || "PARTNER".equals(organisation.type);
}
}
Register validator in hibernate validator by adding fully qualified name of your validator to META-INF/services/javax.validation.ConstraintValidator
file.
Last step is to use validator in your entity:
@PartnerOrganisation
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="partner_id", referencedColumnName="id", insertable = false, updatable = false)
private Organization partner;
-
By using
SQL CHECK CONSTRAINT
if database you uses supports it - just like in the example in https://stackoverflow.com/a/55379219/14231619 -
By modeling PARTNER Organisation and CUSTOMER Organisation as separate entity classes.
Since structure of Organisation for PARTNER and CUSTOMER is the same approach from option 4. seems overcomplicated for the situation. I would recommend going with option 1.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论