Как добавить наследников абстрактного класса в модель Spring MVC в контроллере через Generics?

Есть абстрактный класс User и его наследники: Student, Moderator, Director, HrManager.

@Getter
@Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
public abstract class User implements UserDetails, Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;

    @Transient
    private String matchingPassword;
    private String name;
    private String surname;
    private String patronymic;
    private LocalDate birthday;
    private String hometown;
    private String number;
    private String mail;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private List<Role> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return getRoles();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
@Entity(name = "Student")
@Table(name = "student", schema = "public")
public class Student extends User implements Serializable {

    private String studentIdNumber;
    private String groupNumber;
    private String academicPerformance;
    private String faculty;
    private String department;
    private long rating;

    @Builder
    public Student(Long id,
                   String username,
                   String password,
                   String matchingPassword,
                   String name,
                   String surname,
                   String patronymic,
                   LocalDate birthday,
                   String hometown,
                   String number,
                   String mail,
                   String studentIdNumber,
                   String groupNumber,
                   String academicPerformance,
                   String faculty,
                   String department,
                   List<Role> roles) {
        super(id, username, password, matchingPassword, name, patronymic, surname, birthday, hometown, number, mail, roles);
        this.studentIdNumber = studentIdNumber;
        this.groupNumber = groupNumber;
        this.academicPerformance = academicPerformance;
        this.faculty = faculty;
        this.department = department;
        this.rating = 0;
    }

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
@Entity(name = "HrManager")
@Table(name = "hr_manager", schema = "public")
public class HrManager extends User implements Serializable {

    private String company;

    @Builder
    public HrManager(Long id,
                     String username,
                     String password,
                     String matchingPassword,
                     String name,
                     String surname,
                     String patronymic,
                     LocalDate birthday,
                     String hometown,
                     String number,
                     String mail,
                     String company,
                     List<Role> roles) {
        super(id,
                username,
                password,
                matchingPassword,
                name,
                surname,
                patronymic,
                birthday,
                hometown,
                number,
                mail,
                roles);
        this.company = company;
    }
}

И т. д. еще две сущности.

Использую Spring Data Jpa и Repository для взаимодействия с БД. Есть общие репозиторий для crud-операций и сервис:

@NoRepositoryBean
public interface CommonRepository<U extends User> extends JpaRepository<U, Long> {

    @Query("SELECT u FROM #{#entityName} u")
    List<U> findAll();

    @Query("SELECT u FROM #{#entityName} u WHERE u.id = :id")
    Optional<U> findById(@Param("id") Long id);

    @Query(value = "INSERT u INTO #{#entityName}", 
            nativeQuery = true)
    Optional<U> save(U user);

    @Query("DELETE FROM #{#entityName} u WHERE u.id = :id")
    void deleteById(@Param("id") Long id);

    @Query("SELECT u FROM #{#entityName} u WHERE  u.username = :username")
    U findByUsername(@Param("username") String username);
}

@Service
public class CommonService<U extends User> implements UserDetailsService {

    private CommonRepository<U> commonRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        U user = commonRepository.findByUsername(username);

        if(user != null) {
            Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
            for(Role role : user.getRoles()) {
                grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
            }

            return new org.springframework.security.core.userdetails.User(
                    user.getUsername(),
                    user.getPassword(),
                    grantedAuthorities
            );
        }

        throw new UsernameNotFoundException("Пользователя с логином: " + user.getUsername() + " не существует!");
    }

    public List<U> findAll() {
        return commonRepository.findAll();
    }

    public void save(U user) {
        Optional<U> userFromDB = commonRepository.findById(user.getId());
        if (!userFromDB.isPresent()) {
            commonRepository.save(user);
        }
    }

    public Optional<U> findById(Long id) {
        return Optional.of(commonRepository.findById(id).orElseThrow(NullPointerException::new));
    }

    public void deleteById(Long id) {
        Optional<U> userFromDB = commonRepository.findById(id);
        if(!userFromDB.isPresent()) {
            commonRepository.deleteById(id);
        }
    }
}

Как мне создать объект наследника класса User для регистрации в контроллере:

@Slf4j
@Controller
public class RegistrationController<U extends User> {

    private final CommonService<U> commonService;
    private final SecurityService securityService;

    @Autowired
    public RegistrationController(CommonService<U> commonService, SecurityService securityService) {
        this.commonService = commonService;
        this.securityService = securityService;
    }

    @GetMapping("/registration")
    public String showRegistrationForm(Model model) {
        log.info("showRegistrationForm method called");
        model.addAttribute("newUser", new User());
        model.addAttribute("avatar", "default_avatar.png");
        model.addAttribute("username", "Unknown");

        return "registration";
    }

    @PostMapping("/registration")
    public String registerUserAccount(@ModelAttribute("newUser") @Valid U newUser,
                                      BindingResult bindingResult,
                                      Model model) {
        log.info("registerUserAccount method called");

        if(bindingResult.hasErrors()) {
            log.error("Validation errors: " + bindingResult.hasErrors());
            return "registration";
        }

        if(!newUser.getPassword().equals(newUser.getMatchingPassword())) {
            log.error("Passwords do not match: " + newUser.getPassword() + " != " + newUser.getMatchingPassword());
            model.addAttribute("passwordError", "Пароли не совпадают!");
            return "registration";
        }

        if(commonService.findById(newUser.getId()).isPresent()) {
            log.error("A user with the same name already exists");
            model.addAttribute("usernameError", "Пользователь с таким именем уже существует!");

            return "registration";
        }

        commonService.save(newUser);
        securityService.autoLogin(newUser.getUsername(), newUser.getMatchingPassword());

        log.info("New user registered: " + newUser.toString());

        return "redirect:/login";
    }
}

В метод showRegistrationForm вместо new User, положить new <наследник User> model.addAttribute("newUser", new <наследник User>);

@GetMapping("/registration")
        public String showRegistrationForm(Model model) {
            log.info("showRegistrationForm method called");
            model.addAttribute("newUser", new User());
            model.addAttribute("avatar", "default_avatar.png");
            model.addAttribute("username", "Unknown");

            return "registration";
        }

Ответы (0 шт):