与构造函数的通用一对多关系

问题描述

我有一个使用通用的一对多关系的实体。不幸的是,lobmok @AllArgsConstructor 无法映射/发布 List 扩展到 List 。 EducationTranslation和Translatable的属性完全相同,而EducationTranslation 扩展可翻译。 如果删除自定义构造函数,则会出现以下错误

不能将java.lang.Long字段EducationTranslation.id设置为 可翻译

有没有注释或设计模式可以使我摆脱自己编写的构造函数并使代码更简洁明了?

@OnetoMany(
    targetEntity = EducationTranslation.class,cascade = CascadeType.ALL,fetch = FetchType.LAZY)
private List<? extends Translatable> translationList = new ArrayList<>();


public Education(Long id,Profile profile,Collection<? extends Translatable> translationList) {
    this.id = id;
    this.profile = profile;
    this.translationList = translationList
        .stream()
        .map(x -> new EducationTranslation(x.getId(),x.getCode(),x.getTranslation()))
        .collect(Collectors.toList());
}

解决方法

使用通配符约束的集合对于JPA实体来说有点不方便。

请注意,在初始创建之后,您将无法在不进行强制转换的情况下将任何内容插入声明为List<? extends Translatable>的列表中,这意味着以下代码将不起作用:

Education education = entityManager.find(Education.class,id);
education.getTranslationList().add(new EducationTranslation(...)); // compilation error

它的行为之所以如此,是因为List<? extends Translatable>被解释为“扩展了Translatable的某些未知元素类型的列表”,并且编译器不允许您的代码向该列表中添加任何内容,不知道确切的类型?代表什么。我想您会希望.add(new EducationTranslation())是合法的,因为EducationTranslation 确实扩展了Translatable,但这是为什么不能允许它的更好说明。 :

List<Dog> dogs = new ArrayList<>(List.of(new Dog())); //just a mutable collection with a Dog inside
List<? extends Animal> someAnimals = dogs; //a list of Dogs is a list of 'a type that extends Animal',so - valid assignment
someAnimals.add(new Cat()); //hey,a Cat does extend Animal,so this should be legal,right?
Dog shapeshifter = dogs.get(1); //...right???

总而言之,简单地将translationList声明为List<EducationTranslation>将会更加方便。

为什么我可以最初添加元素并将其保存在日期存储中,但是在尝试使用相同的对象签名添加元素时却出现编译错误

您没有使用“相同签名”。您的代码在任何时候都调用.add()。它仅构造一个有效类型为List<EducationTranslation>的List,然后将其分配给声明为List<? extends Translatable>的字段。问题是,由于声明字段的方式,而不是您为其分配的内容,因此无法使用存储在该字段中的引用来调用.add(new EducationTranslation())。与以下代码段中的场景几乎相同:

Object obj = "Hello";
obj.length(); // you know obj points to a String,but you assign it to a reference with a more general type,so this call is illegal

请注意,如果要从Education类的 client 隐藏列表元素的具体类型,则以下代码仍然合法:

interface TranslationHolder {

    List<? extends Translatable> getTranslationList();
}

class Education implements TranslationHolder {

    private List<EducationTranslation> translationList = new ArrayList<>();

    //this getter is a valid implementation for TranslationHolder.getTranslationList()
    public List<EducationTranslation> getTranslationList() {
        return translationList;
    }
}

class ClassThatUsesEducationButOnlyKnowsItAsTranslationHolder  {

   public void execute() {
        TranslationHolder holder = getEducationAsTranslationHolderInSomeIndirectWay();
        holder.getTranslationList(); // this still return a reference to List<? extends Translatable>,NOT List<EducationTranslation>
   }
}

因此,如果要从EducationTranslation类的 client 中提取Education,则无需将字段本身声明为通配符列表