在JPA中实现互斥关系的最佳方法是什么?

时间:2018-08-25 11:19:09

标签: java hibernate jpa

我正在尝试建立功能位置层次结构,我从 国家/地区 模型/实体开始。下一层是与国家/地区具有 ManyToOne 关系的 实体(双向)。现在,我有 City 实体,它是与省(双向)绑定的 ManyToOne ,但是这种关系可以为空。但是现在,如果City-Province关系为空,则需要City-Country关系(也可以为空)。因此,至少需要一个。但是,如果这两个条件之一已经存在,则无法输入其他条件。

因此,基本上,城市中的这两个关系(城市,省/国家/地区)是互斥,但至少需要其中之一。我正在使用 Java JPA(休眠),但我没有发现实现此功能的任何特殊功能。有没有行之有效的方法?

1 个答案:

答案 0 :(得分:0)

您不能使用JPA或Hibernate为互斥关联建模。但是您可以为2个独立模型建模并添加BeanValidation约束。 JPA集成了BeanValidation规范,并在持久或更新实体之前自动触发验证。

我在Hibernate Tips之一中对此进行了更详细的说明。这是简写​​形式。

您可以在City实体上为2个关联建模。

@Entity
@EitherOr
public class City {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Version
    private int version;

    @ManyToOne
    private Province province;

    @ManyToOne
    private Country country;

    ...
}

请注意第2行中的@EitherOr批注。这是我根据BeanValidation规范实现的自定义约束。 @Constraint注释指定将在实体生命周期过渡期间触发的ConstraintValidator

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {EitherOrValidator.class})
public @interface EitherOr {

    String message() default "A city can only be linked to a country or a province.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

}

ConstraintValidator的实现非常简单。您需要实现isValid方法,并检查provincecountry属性是否不为空。

public class EitherOrValidator implements ConstraintValidator<EitherOr, City>{

    @Override
    public void initialize(EitherOr arg0) { }

    @Override
    public boolean isValid(City city, ConstraintValidatorContext ctx) {
        return (city.getProvince() == null && city.getCountry() != null) 
                || (city.getProvince() != null && city.getCountry() == null);
    }


}