有没有办法让通用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()
返回 BOTH Capybaras和Dogs。这个answer解释了:
仅当域类使用单表继承时才有效。关于我们可以在bootstrap时获得的域类的唯一信息是它将是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();
}
...
}
如果存储库根据类型findAll()
确实无法处理<T>
,那么拥有单个AnimalService最简单的方法是什么?当然,必须有一种比我做得更好的方式,因为如果你在服务中有更多的复杂性而且超过几只动物,它会变得非常丑陋,非常快。
AnimalRepository<Dog>
and AnimalRepository<Capybara>
as being the same thing(将同一个存储库注入使用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);
}
}
可能有更好的方法,所以我会对建议持开放态度。
答案 0 :(得分:2)
在存储库上:由于您不使用单表继承,因此每个T
都需要一个存储库实例,而无法解决这个问题。这意味着您将拥有AnimalRepository<Dog>
的实例和AnimalRepository<Capybara>
的实例。
在服务上:在应用程序的某个地方,您需要一个开关/案例,根据类型将您引导到正确的存储库。
您有两个选项,a)具有处理所有类型的单个服务实例,并在内部将查询定向到相应的存储库,或b)为每个T
提供服务实例并在其他地方选择实例。 / p>
我更喜欢选项a)。您可以通过获取findAll()
类型的引用并使用开关/案例来选择适当的存储库来调整当前实现以使用单个T
返回正确的类型。
在运行时获取<T>
的引用因类型擦除而变得混乱,但可以完成。
答案 1 :(得分:0)
如何向Animal添加类别属性/枚举
@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类型,但这有点棘手