You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调整类实现,在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 21:07:49