Here is a simplified working code. There are a mapped superclass and two its subclasses (in real life superclass of course contains more fields)
Animal.java
@MappedSuperclass
@lombok.NoArgsConstructor
@lombok.RequiredArgsConstructor
public abstract class Animal {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@lombok.Getter
private Long id;
@lombok.Getter
@lombok.NonNull
private String name;
}
Cat.java
@Entity
@Table
@lombok.NoArgsConstructor
public class Cat extends Animal {
public Cat(Integer weight, String name) {
super(name);
this.weight = weight;
}
@lombok.Getter
private Integer weight;
}
Dog.java
@Entity
@Table
@lombok.NoArgsConstructor
public class Dog extends Animal {
public Dog(Integer age, String name) {
super(name);
this.age = age;
}
@lombok.Getter
private Integer age;
}
AnimalRepositoryImpl
and AnimalRepository
contain some shared code for Cat and Dog repositories.
AnimalRepository.java
@NoRepositoryBean
public interface AnimalRepository<T extends Animal> extends JpaRepository<T, Long> {
List<T> findAllByName(String name);
}
AnimalRepositoryImpl.java
public class AnimalRepositoryImpl<T extends Animal> {
@Autowired
AnimalRepository<T> animalRepository;
public List<T> findAllBySomeLogic() {
return animalRepository.findAll().stream().filter(animal -> !animal.getName().startsWith("Z")).collect(Collectors.toList());
}
}
Now I can add all CatRepositories and it still works (and works correctly).
CatRepository.java
@Transactional
public interface CatRepository extends AnimalRepository<Cat>, CatRepositoryCustom {
}
CatRepositoryCustom.java
public interface CatRepositoryCustom {
public List<Cat> findAllBySomeLogic();
}
CatRepositoryImpl.java
public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {
}
Here is a test class which still uses only cat repository.
AnimalRepositoryTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@ActiveProfiles(profiles = "test")
public class AnimalRepositoryTest {
@After
public void tearDown() {
catRepository.deleteAll();
}
@Autowired
private CatRepository catRepository;
@Test
public void shouldFindAllBySomeLogic() {
// given
catRepository.save(Lists.newArrayList(new Cat(2000, "Luna"), new Cat(2500, "Zoe"), new Cat(1800, "Toby")));
// when
List<Cat> cats = catRepository.findAllBySomeLogic();
// then
assertThat(cats.stream().map(c -> c.getName()).collect(Collectors.toList()), containsInAnyOrder("Luna", "Toby"));
}
@Test
public void shouldFindAllByName() {
// given
catRepository.save(Lists.newArrayList(new Cat(2000, "Luna"), new Cat(2500, "Zoe"), new Cat(1800, "Toby")));
// when
List<Cat> cats = catRepository.findAllByName("Luna");
// then
assertThat(cats.stream().map(c -> c.getName()).collect(Collectors.toList()), containsInAnyOrder("Luna"));
}
}
The way I've coded it was inspired mostly by this question (but my case is more complicated).
So... the main question. - How to add repositories for Dog
(almost identical to Cat
ones) and not to get something like NoUniqueBeanDefinitionException: No qualifying bean of type...
? I've tried some variations with @Qualifier
but seems it doesn't work in this case. Or maybe I'm doing it completely wrong.