[英]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.