Spring 使用 MappedSuperclass 和子类为 Spring 数据存储库启动自定义实现

Spring Boot custom implementations for Spring Data repositories with MappedSuperclass and subclasses

这是一个简化的工作代码。有一个映射的 superclass 和两个它的 subclasses(在现实生活中 superclass 当然包含更多字段)

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;
}

AnimalRepositoryImplAnimalRepository 包含 Cat 和 Dog 存储库的一些共享代码。

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());
    }
}

现在我可以添加所有 CatRepositories,它仍然有效(并且正常工作)。

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 {
}

这是一个测试 class,它仍然只使用 cat 存储库。

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"));
    }
}

我的编码方式主要受到 this question 的启发(但我的情况更复杂)。

所以...主要问题。 - 如何为 Dog 添加存储库(几乎与 Cat 相同)而不是像 NoUniqueBeanDefinitionException: No qualifying bean of type... 这样的东西?我尝试了 @Qualifier 的一些变体,但似乎在这种情况下不起作用。或者也许我做的完全错了。

我看到至少一个与您的 classes 的通用定义相关的失败。 class CatRepositoryImpl 扩展了 classe AnimalRepositoryImpl 而没有任何通用类型。 (请参阅您 post 的以下两个代码片段)

public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {

}

public class AnimalRepositoryImpl<T extends Animal> {

}

在我看来应该是这样的。

public class CatRepositoryImpl extends AnimalRepositoryImpl<Cat> implements CatRepositoryCustom {

}

除此之外,我会避免在存储库中做与逻辑相关的事情 class 并将其移至服务级别。