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

huangapple go评论76阅读模式
英文:

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

问题

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

我有两个实体:DatasetOrganizationDataset 实体与 Organization 实体之间有多对一的映射关系。组织分为两种类型:CustomerPartner。业务要求是,只有属于 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 列将包含 PARTNERCUSTOMER

这是我当前设计的 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 = &quot;organization&quot;)
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 = &quot;created_date&quot;)
	@Temporal(value =  TemporalType.TIMESTAMP)
	private Date createdDate;

	@OneToMany(fetch = FetchType.LAZY)
	@JoinColumn(name = &quot;organization_id&quot;, insertable = false, updatable = false)
	private List&lt;User&gt; users;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumns({ @JoinColumn(name = &quot;subtype&quot;, referencedColumnName = &quot;name&quot;, insertable = false, updatable = false),
			@JoinColumn(name = &quot;type&quot;, referencedColumnName = &quot;organization_type&quot;, 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=&quot;datasets&quot;)
public class Dataset {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private long id;
	
	@Column(name=&quot;partner_id&quot;, 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=&quot;partner_id&quot;, referencedColumnName=&quot;id&quot;, 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

有几种方法可以实现这个目标:

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

  2. 通过利用 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;
  1. 如果您使用的数据库支持,可以通过使用 SQL CHECK CONSTRAINT 来实现,就像 https://stackoverflow.com/a/55379219/14231619 中的示例一样。

  2. 将 PARTNER 组织和 CUSTOMER 组织建模为单独的实体类。由于 PARTNER 和 CUSTOMER 的组织结构相同,选项 4 的方法对于这种情况似乎过于复杂。我建议选择选项 1。

英文:

There are several option how you can achieve it:

  1. By adding validation at business layer in the place when you build new Dataset object prior to persisting it. Such validation would check if given Organisation can be associated with created Dataset entity based on organisation type.

  2. 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

&lt;dependency&gt;
  &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
  &lt;artifactId&gt;hibernate-validator&lt;/artifactId&gt;
  &lt;version&gt;5.3.4.Final&lt;/version&gt;
&lt;/dependency&gt;

Define annotation for new validation

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

Define contraint validator

public class PartnerOrganisationValidator implements ContraintValidator&lt;PartnerOrganisation, Organisation&gt; {
    @Override
    public boolean isValid(Organisation organisation, ConstraintValidatorContext constraintValidatorContext) {
        return organisation == null || &quot;PARTNER&quot;.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=&quot;partner_id&quot;, referencedColumnName=&quot;id&quot;, insertable = false, updatable = false)
    private Organization partner;
  1. By using SQL CHECK CONSTRAINT if database you uses supports it - just like in the example in https://stackoverflow.com/a/55379219/14231619

  2. 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.

huangapple
  • 本文由 发表于 2020年9月8日 16:10:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/63789754.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定