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?
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 given Organisation
can be associated with created Dataset
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.