2

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.

Community
  • 1
  • 1
ytterrr
  • 3,036
  • 6
  • 23
  • 32
  • do you really need the impl classes for this example (i.e. mean to demonstrate the issue)? – Ueli Hofstetter Apr 30 '15 at 20:49
  • @chuchikaeschtli yes, I do. Of course, the logic of `findAllBySomeLogic()` method can easily be implemented by a simple query - this is just an example. But in real life it is necessary to provide a custom implementation for some complex repository methods. – ytterrr May 01 '15 at 05:35

1 Answers1

1

I see at least one failure related to the generic definition of your classes. The class CatRepositoryImpl extends the classe AnimalRepositoryImpl without any generic Types. (See the following two code snippets of your post)

public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {

}

public class AnimalRepositoryImpl<T extends Animal> {

}

In my opinion it should look like.

public class CatRepositoryImpl extends AnimalRepositoryImpl<Cat> implements CatRepositoryCustom {

}

Beside that, I would avoid doing logic related things in a Repository class and move it to a Service level.

mh-dev
  • 5,264
  • 4
  • 25
  • 23