简体   繁体   中英

How to pass generics to an Spring CRUD Repository's save method

Let's say we have three JPA objects with name and id. I made an interface with getters+setters for name and id.

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

We also have three repositories for these JPA objects:

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

For each of these objects we want to run the same method in an spring service. (highly simplified)

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);
    }
}

Which is callable by the same spring service by

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

This results in the error:

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

Which is odd if I look at Springs CRUD Repository: CrudRepository<T, ID> and its save method: <S extends T> S save(S entity); .

In our example we have T = ? extends MetadataObject T = ? extends MetadataObject and S = ? extends ? extends MetadataObjects S = ? extends ? extends MetadataObjects S = ? extends ? extends MetadataObjects .

If we change my function to private void importMetadata(CrudRepository<MetadataObject, String> bdbRepository) the save method is correct but I can't call the method with my carRepository anymore

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

Be aware: I highly simplified the example. I know that in this example Interfaces for these JPA classes makes no sense. I also know that my method makes no sense but it highlights the problem perfectly.

My question would be: What to pass to save or how to rewrite this function that it works? What exactly is the issue here?

You can use this method definition:

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);
   }
} 

I will try the explain the reason the method save of CrudRepository<? extends MetadataObject, String> mRepository CrudRepository<? extends MetadataObject, String> mRepository fails.

Suppose we have a generic class:

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

When we write something like:

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

the compiler complains about the c.save line.

That's because, when applying the type restriction, the compile doesn't know whether the c reference points, in fact, to C<Object> or C<Number> or whatever, since both C<Object> and C<Number> are accepted as arguments for f .

Because of that, the compiler doesn't know whether the save method's argument is allowed or not, hence the error.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM