[英]Generic Spring Data JPA Repository findAll
有没有一种方法可以使通用Spring Data JPA存储库正确处理诸如findAll()
类的方法? 例如AnimalRepository<Dog>.findAll
只返回狗,而不是所有动物? 或者至少,最好的解决方法是什么?
说我有这个:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Animal extends BaseEntity {
...
}
@Entity
public class Dog extends Animal {
...
}
@Entity
public class Capybara extends Animal {
...
}
public interface AnimalRepository<T extends Animal> extends JpaRepository<T, Long> {
}
@Service("animalService")
public class AnimalServiceImpl<T extends Animal> implements AnimalService<T> {
private final AnimalRepository<T> animalRepository;
@Autowired
public AnimalServiceImpl(AnimalRepository<T> aR) {
this.animalRepository = aR;
}
@Override
public void save(T animal) {
animalRepository.save(animal);
}
@Override
public List<T> findAll() {
return animalRepository.findAll();
}
...
}
它几乎完美地工作,将每只动物保存在自己的桌子上,等等。 唯一的问题是: findAll()
同时返回水豚和狗。 该答案说明:
仅当域类使用单表继承时,此方法才有效。 我们在引导时可以获得的有关域类的唯一信息是它将是Product对象。 因此,对于诸如findAll()甚至findByName(...)之类的方法,相关查询将从Product p其中...的select p开始。 这是由于这样的事实,除非您创建专用的存储库接口以捕获具体的类型信息,否则反射查找将永远无法产生Wine或Car。
好的,可悲的是,使用多个存储库而不是只有一个存储库,代码变得不太干净。 但是我仍然想保留一个AnimalService类。 这是我的方法:
@Service("animalService")
public class AnimalServiceImpl<T extends Animal> implements AnimalService<T> {
private final DogRepository dogRepository;
private final CapybaraRepository capybaraRepository;
@Autowired
public AnimalServiceImpl(AnimalRepository<T> aR) {
this.animalRepository = aR;
}
@Override
public void save(T animal) {
animalRepository.save(animal);
}
@Override
public List<T> findAllDogs() {
return dogRepository.findAll();
}
@Override
public List<T> findAllCapybaras() {
return capybaraRepository.findAll();
}
...
}
如果存储库确实不能根据类型<T>
处理findAll()
,那么拥有单个AnimalService的最干净方法是什么? 当然,必须有一种比我做的更好的方法,因为如果您在服务中具有更多的复杂性,并且有很多动物,那么它将变得非常丑陋,非常快。
编辑:考虑到Spring的DI认为AnimalRepository<Dog>
和AnimalRepository<Capybara>
是同一件事 (将相同的存储库注入使用AnimalRepository<Capybara>
的服务和使用Dog的服务),我不得不创建一个不同的对象它们各自的存储库和服务(@ESala答案的选项B):
@NoRepositoryBean
public interface AnimalRepository<T extends Animal> extends JpaRepository<T, Long> {
}
public interface DogRepository extends AnimalRepository<Dog> {
}
public interface CapybaraRepository extends AnimalRepository<Capybara> {
}
public abstract class AnimalService<T extends Animal> {
private final AnimalRepository<T> animalRepository;
AnimalService(AnimalRepository<T> repo) {
this.animalRepository = repo;
}
public void salvar(T palavra) {
animalRepository.save(palavra);
}
public List<T> findAll() {
return animalRepository.findAll();
}
}
@Service
public class DogService extends AnimalService<Dog> {
@Autowired
public DogService(DogRepository repo) {
super(repo);
}
}
@Service
public class CapybaraService extends AnimalService<Capybara> {
@Autowired
public CapybaraService(CapybaraRepository repo) {
super(repo);
}
}
可能有更好的方法,所以我将保持开放的态度。
在存储库上:由于您不使用单表继承,因此您将需要为每个T
都需要一个存储库实例,没有办法解决。 这意味着您将拥有AnimalRepository<Dog>
的实例和AnimalRepository<Capybara>
AnimalRepository<Dog>
的实例。
在服务上:应用程序中的某个位置,您将需要一个开关/外壳,根据类型将您引导至正确的存储库。
您有2个选择,a)具有处理所有类型的单个服务实例,并在内部将查询定向到相应的存储库,或者b)具有每个T
的服务实例并在其他位置选择该实例。
我更喜欢选项a)。 您可以通过获取对T
类型的引用并使用开关/案例来选择适当的存储库,使当前的实现通过单个findAll()
返回正确的类型。
由于类型擦除,在运行时获取对<T>
的引用很麻烦,但是可以做到。
如何为动物添加类别属性/枚举
@Override
public List<T> findAll(Animal animal) {
if(animal.getCategory() == "Dog")
return findAllDogs();
if(animal.getCategory() == "Capybara")
return findAllCapybaras();
}
@Override
private List<T> findAllDogs() {
return dogRepository.findAll();
}
@Override
private List<T> findAllCapybaras() {
return capybaraRepository.findAll();
}
注意:也可以获取泛型的Class类型,但这有点棘手
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.