【发布时间】:2018-06-15 23:22:17
【问题描述】:
有没有办法让通用 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解释道:
这仅在域类使用单表继承时才有效。我们可以在引导时获得的关于域类的唯一信息是它将是 Product 对象。因此,对于 findAll() 甚至 findByName(...) 等方法,相关查询将以 select p from Product p where... 开始。这是因为反射查找永远无法生成 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> 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);
}
}
可能有更好的方法,所以我会继续接受建议。
【问题讨论】:
标签: java spring architecture spring-data-jpa