如何将泛型传递到Spring CRUD存储库的save方法

时间:2018-12-14 09:00:04

标签: java spring generics spring-data

假设我们有三个名称和ID的JPA对象。我用getters + setter建立了一个接口来输入名称和ID。

class Car implements MetadataObject
class Bus implements MetadataObject
class Train implements MetadataObject

对于这些JPA对象,我们还有三个存储库:

interface CarRepository extends CrudRepository<Car, Long>
interface BusRepository extends CrudRepository<Bus, Long>
interface TrainRepository extends CrudRepository<Train, Long>

对于每个这些对象,我们希望在spring服务中运行相同的方法。 (高度简化)

private void importMetadata(CrudRepository<? extends MetadataObject, String> mRepository) {
    Optional<? extends MetadataObject> currentOptional = mRepository.findById(1);

    if (currentOptional.isPresent()) {
        MetadataObject current = (MetadataObject) currentOptional.get();
        current.setName("a1");
        mRepository.save(current);
    }
}

哪个可以被同一spring服务调用

@Autowired
private CarRepository carRepository;
...
importMetadata(carRepository);

这会导致错误:

The method save(S) in the type CrudRepository<capture#4-of ? extends MetadataObject, Long> is not applicable for the arguments (MetadataObject)

如果我查看Springs CRUD存储库:CrudRepository<T, ID>及其保存方法:<S extends T> S save(S entity);,那会很奇怪。

在我们的示例中,我们有T = ? extends MetadataObjectS = ? extends ? extends MetadataObjects

如果我们将函数更改为private void importMetadata(CrudRepository<MetadataObject, String> bdbRepository),则save方法是正确的,但我无法再通过carRepository调用该方法

The method importMetadata(CrudRepository<MetadataObject,String>) in the type <...> is not applicable for the arguments (CarRepository)

请注意:我高度简化了示例。我知道在此示例中,这些JPA类的接口没有任何意义。我也知道我的方法没有意义,但可以完美地突出问题。

我的问题是:保存什么内容或如何重写此功能?这里到底是什么问题?

2 个答案:

答案 0 :(得分:3)

您可以使用以下方法定义:

private void <T extends MetadataObject>importMetadata(CrudRepository<T, String> mRepository) {
   Optional<T> currentOptional = mRepository.findById(1);

   if (currentOptional.isPresent()) {
       T current = currentOptional.get();
       current.setName("a1");
       mRepository.save(current);
   }
} 

答案 1 :(得分:0)

我将尝试解释save的方法CrudRepository<? extends MetadataObject, String> mRepository失败的原因。

假设我们有一个通用类:

class C<T> {
public void save (T t) {
// .. whatever
}
}

当我们写类似的东西时:

void f (C<? extends Object> c) {
    c.save(new Object());
}

编译器抱怨c.save行。

这是因为,在应用类型限制时,编译器并不知道c引用实际上是指向C<Object>还是C<Number>或其他任何东西,因为{{ 1}}和C<Object>被接受为C<Number>的参数。

因此,编译器不知道是否允许使用f方法的参数,因此会出错。