如何调整类实现,在UserService中注入自定义UserRepository而非JpaRepository?
解决自定义Repository方法在Service中无法调用的问题
嘿,我明白你的困扰了——你在UserService里想调用UserRepository的自定义方法findUserByLogin,但父类CrudAbstractService里注入的是JpaRepository类型,导致没法直接用对吧?这是个典型的父类引用无法调用子类自定义方法的问题,下面给你两种优雅的解决办法:
方法一:给抽象类加泛型Repository参数(最推荐)
这个方案能保持代码的类型安全,还能让抽象类更灵活适配不同的Repository。
第一步:修改CrudAbstractService
把原来固定的JpaRepository类型换成泛型参数,让每个子类可以指定自己的Repository类型:
public abstract class CrudAbstractService< ENTITY extends EntityInterface, DTO extends DTOInterface, REPO extends JpaRepository<ENTITY, Long> // 新增泛型,限定为JpaRepository的子类 > { protected final REPO repository; // 替换原来的JpaRepository为泛型REPO protected final Validator<DTO> validator; protected final MapperInterface<ENTITY, DTO> mapper; private Class<ENTITY> entityClazz; // 构造方法的参数也改成泛型REPO public CrudAbstractService(REPO repository, Validator<DTO> validator, MapperInterface<ENTITY, DTO> mapper) { this.repository = repository; this.validator = validator; this.mapper = mapper; } // 原有的CRUD方法完全不用改,因为REPO是JpaRepository的子类,肯定有这些方法 public Iterable<DTO> findAll() { List<ENTITY> allEntities = repository.findAll(); if (allEntities == null) { throw new EntityNotFound(entityClazz); } List<DTO> mappedDTOs = mapper.toDTOs(allEntities); return mappedDTOs; } // delete和save方法保持原样即可 }
第二步:修改UserService
继承的时候指定第三个泛型参数为UserRepository<UserEntity, Long>,这样就能直接用repository调用自定义方法了:
@Service public class UserService extends CrudAbstractService<UserEntity, UserDTO, UserRepository<UserEntity, Long>> { private MapperInterface<LectureEntity,LectureDTO> lectureMapper; // 构造方法参数还是UserRepository,父类会自动把它当成泛型REPO类型 public UserService(UserRepository<UserEntity, Long> repository, Validator<UserDTO> validator, MapperInterface<UserEntity, UserDTO> mapper, MapperInterface<LectureEntity,LectureDTO> lectureMapper) { super(repository, validator, mapper); this.lectureMapper = lectureMapper; } public UserDTO findUserByLogin(String login) { if (login == null) { throw new UserNotFoundException(); } // 现在直接用repository调用自定义方法,完全没有类型问题 Optional<UserEntity> userByLogin = repository.findUserByLogin(login); UserEntity userEntity = userByLogin.orElseThrow(UserNotFoundException::new); List<LectureEntity> reservations = userEntity.getReservations(); List<LectureDTO> lectureDTOS = lectureMapper.toDTOs(reservations); UserDTO userDTO = mapper.toDTO(userEntity); userDTO.setLectures(lectureDTOS); return userDTO; } }
方法二:在UserService中单独注入UserRepository(不推荐)
如果你不想动抽象类,也可以在UserService里单独注入UserRepository,绕过父类的字段:
@Service public class UserService extends CrudAbstractService<UserEntity,UserDTO> { private final UserRepository<UserEntity, Long> userRepository; // 新增自己的Repository字段 private MapperInterface<LectureEntity,LectureDTO> lectureMapper; // 构造方法同时注入父类需要的JpaRepository和自己的UserRepository(Spring会自动注入同一个实例) public UserService(UserRepository<UserEntity, Long> userRepository, Validator<UserDTO> validator, MapperInterface<UserEntity, UserDTO> mapper, MapperInterface<LectureEntity,LectureDTO> lectureMapper) { super(userRepository, validator, mapper); this.userRepository = userRepository; this.lectureMapper = lectureMapper; } public UserDTO findUserByLogin(String login) { if (login == null) { throw new UserNotFoundException(); } // 用自己注入的userRepository调用自定义方法 Optional<UserEntity> userByLogin = userRepository.findUserByLogin(login); // 后续逻辑不变 } }
这个方法虽然能解决问题,但有点冗余,毕竟父类已经有了Repository的引用,重复注入显得不够优雅,所以还是更推荐第一种方案。
为啥原来的代码不行?
简单说就是Java的多态规则:父类类型的引用(JpaRepository)可以指向子类实例(UserRepository),但只能调用父类里定义的方法,子类的自定义方法是看不到的。只有当引用的类型是UserRepository时,才能调用findUserByLogin。
内容的提问来源于stack exchange,提问作者Tomek Borkowski
相关产品推荐
相关产品推荐

