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;
}
AnimalRepositoryImpl
和 AnimalRepository
包含 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 并将其移至服务级别。
这是一个简化的工作代码。有一个映射的 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;
}
AnimalRepositoryImpl
和 AnimalRepository
包含 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 并将其移至服务级别。