Используйте несколько столбцов в качестве фильтров при извлечении записей таблицы с помощью JPA и Spring Boot

#java #spring #spring-boot #jpa #spring-data-jpa

Вопрос:

Я создаю одно приложение Spring Boot, содержащее одну сущность пользователя.

Я хочу реализовать функциональность, в которой администратор может искать пользователей с помощью разных фильтров, и фильтры могут быть одним или несколькими столбцами из таблицы пользователей.

Ниже приведен класс сущности пользователя:

 @Getter
@Setter
@Entity
@Table(name = "users")
public class UserEntity {

    @Id
    @Column(name = "user_id", nullable = false, unique = true, length = 10)
    private String userId;

    @Column(name = "user_first_name", nullable = false, length = 20)
    private String userFirstName;

    @Column(name = "user_middle_name", length = 20)
    private String userMiddleName;

    @Column(name = "user_last_name", nullable = false, length = 20)
    private String userLastName;

    @Column(name = "user_contact", nullable = false, unique = true, length = 10)
    private Long userContact;

    @Column(name = "user_email", nullable = false, unique = true, length = 50)
    private String userEmail;

    @Column(name = "user_password", nullable = false)
    private String userPassword;

    @Column(name = "user_enabled", nullable = false)
    private Boolean userEnabled;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<RoleEntity> userRoles;

    @OneToMany(mappedBy = "tokenUser", fetch = FetchType.LAZY)
    private Set<TokenEntity> userTokens;

    @Temporal(TemporalType.DATE)
    @Column(name = "last_updated_on", nullable = false)
    private Date lastUpdatedOn;

    @Column(name = "last_updated_by", nullable = false)
    private String lastUpdatedBy;

    @Temporal(TemporalType.DATE)
    @Column(name = "created_on", nullable = false)
    private Date createdOn;

    @Column(name = "created_by", nullable = false)
    private String createdBy;
}

 

При поиске пользователя фильтром может быть только имя, или в какой-то момент и имя, и фамилия могут быть фильтром, или, если требуется, некоторые другие значения столбцов (идентификатор, адрес электронной почты и Контакт) также могут использоваться в качестве фильтров вместе с ними. Выбор фильтров будет полностью зависеть от администратора во время выполнения.

Может ли кто-нибудь, пожалуйста, рассказать мне, как я могу реализовать эту функциональность с помощью JPA и Spring Boot?

Ниже приведены мои классы Контроллера, Службы и Репозитория соответственно.

Пользовательский контроллер

 @RestController
@RequestMapping("/bma/api/admin/users")
public class UserController {

    @Autowired
    private UserService userService;

    private final int DEFAULT_PAGE_NUMBER = 1;
    private final int DEFAULT_PAGE_SIZE = 5;

    @GetMapping("/all")
    public ResponseEntity<?> getAllUsers(
            @RequestParam(name = "page_number", required = false) Integer pageNumber,
            @RequestParam(name = "page_size", required = false) Integer pageSize) {
        try {
            if (pageNumber == null) pageNumber = DEFAULT_PAGE_NUMBER;
            if (pageSize == null) pageSize = DEFAULT_PAGE_SIZE;
            List<UserResponseModel> responseModels = userService.getAllUserList(pageNumber-1, pageSize);
            GetAllDataResponseMetadata metadata = new GetAllDataResponseMetadata(HttpStatus.FOUND.value(),
                    pageNumber, pageSize);
            return ResponseEntity.status(HttpStatus.FOUND).body(new SuccessResponse<>(metadata, responseModels));
        }
        catch (NoRecordAvailableException exception) {
            CommonResponseMetadataWithMessage metadata =
                    new CommonResponseMetadataWithMessage(HttpStatus.NOT_FOUND.value(), exception.getMessage());
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponse<>(metadata));
        }
    }
}
 

UserService

 @Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RoleRepository roleRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public UserResponseModel addNewUser(UserRequestModel userRequestModel) {
        if (userRepository.existsUserEntityByUserContact(userRequestModel.getUserContact())) {
            throw new InformationAlreadyExistsException("Contact Number Already Exists");
        }
        else if (userRepository.existsUserEntityByUserEmail(userRequestModel.getUserEmail())) {
            throw new InformationAlreadyExistsException("Email Already Exists");
        }
        else {
            UserDetailsValidationUtility.isEmailValid(userRequestModel.getUserEmail());
            UserDetailsValidationUtility.isContactNumberValid(userRequestModel.getUserContact());
            UserDetailsValidationUtility.isNameValid(userRequestModel.getUserFirstName(), false);
            UserDetailsValidationUtility.isNameValid(userRequestModel.getUserLastName(), false);
            UserDetailsValidationUtility.isNameValid(userRequestModel.getUserMiddleName(), true);
            UserDetailsValidationUtility.isPasswordValid(userRequestModel.getUserPassword());

            UserEntity newUserEntity = getUserEntityFromModel(userRequestModel);
            return getUserResponseModelFromEntity(userRepository.save(newUserEntity));
        }
    }

    public List<UserResponseModel> getAllUserList(int pageNumber, int PageSize) {
        Pageable pageWithSpecificNumberOfRecord = PageRequest.of(pageNumber, PageSize);
        Page<UserEntity> userEntities = userRepository.findAll(pageWithSpecificNumberOfRecord);
        if (userEntities.isEmpty()) {
            throw new NoRecordAvailableException("No User Record Available");
        }
        else {
            List<UserResponseModel> userResponseModels = new ArrayList<>();
            userEntities.forEach(userEntity ->
                    userResponseModels.add(getUserResponseModelFromEntity(userEntity)));
            return userResponseModels;
        }
    }

    private UserResponseModel getUserResponseModelFromEntity(UserEntity existingUserEntity) {
        UserResponseModel responseModel = new UserResponseModel();
        responseModel.setUserId(existingUserEntity.getUserId());
        responseModel.setUserFullName(buildFullNameOfUser(existingUserEntity));
        responseModel.setUserContact(existingUserEntity.getUserContact());
        responseModel.setUserEmail(existingUserEntity.getUserEmail());
        return responseModel;
    }

    private UserEntity getUserEntityFromModel(UserRequestModel userRequestModel) {
        Date currentDateTime = BankManagementUtility.getCurrentDateTime();
        String userId = generateUserId();

        UserEntity newUserEntity = new UserEntity();
        newUserEntity.setUserId(userId);
        newUserEntity.setUserFirstName(userRequestModel.getUserFirstName().trim());
        if (userRequestModel.getUserMiddleName() == null)
            newUserEntity.setUserMiddleName("");
        else newUserEntity.setUserMiddleName(userRequestModel.getUserMiddleName().trim());
        newUserEntity.setUserLastName(userRequestModel.getUserLastName().trim());
        newUserEntity.setUserContact(userRequestModel.getUserContact());
        newUserEntity.setUserEmail(userRequestModel.getUserEmail().trim());
        newUserEntity.setUserPassword(passwordEncoder.encode(userRequestModel.getUserPassword().trim()));
        newUserEntity.setUserEnabled(false);
        newUserEntity.setLastUpdatedOn(currentDateTime);
        newUserEntity.setLastUpdatedBy(userId);
        newUserEntity.setCreatedOn(currentDateTime);
        newUserEntity.setCreatedBy(userId);

        newUserEntity.setUserTokens(new HashSet<>());
        newUserEntity.setUserRoles(new HashSet<>());
        newUserEntity.getUserRoles().add(roleRepository.findRoleEntityByRoleName("USER"));

        return newUserEntity;
    }

    private String generateUserId() {
        StringBuilder userId = new StringBuilder("USER");
        userId.append(BankManagementUtility.generateNumberIdNumberByDigit(6));
        if (userRepository.existsUserEntityByUserId(userId.toString())) {
            return generateUserId();
        }
        else return userId.toString();
    }

    private String buildFullNameOfUser(UserEntity userEntity) {
        StringBuilder fullName = new StringBuilder(userEntity.getUserFirstName());
        if (!userEntity.getUserMiddleName().isEmpty()) {
            fullName.append(" ");
            fullName.append(userEntity.getUserMiddleName());
        }
        fullName.append(" ");
        fullName.append(userEntity.getUserLastName());
        return fullName.toString();
    }
}
 

Информация для пользователей

 @Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {

    UserEntity findUserEntityByUserEmail(String email);
    UserEntity findUserEntityByUserId(String id);

    Boolean existsUserEntityByUserId(String id);
    Boolean existsUserEntityByUserEmail(String email);
    Boolean existsUserEntityByUserContact(Long contact);
}
 

Также вот ссылка на репозиторий GitHub, если кто — то хочет взглянуть на весь проект:

https://github.com/rajeshsinha1997/Centralised_Banking_System_Application

Помимо вышеупомянутого запроса, если у кого-то есть какие-либо другие советы/предложения относительно кода или проекта, это будет оценено.

Ответ №1:

Я думаю, что лучший способ для вас-создать другую конечную точку, которая получает объект JSON с критериями поиска. Например:

 {
    "userFilter": {
        "userId": "1",
        "userFirstName": "John",
        "userLastName": "Doe",
        "userEmail": "john.doe@johndoe.com"
    }
}
 

В конечной точке замените все атрибуты «null» на «*». Таким образом, вы можете сгенерировать запрос, в котором все данные будут возвращены для определенного атрибута, если он не входит в параметры фильтра.

 @PostMapping("/filter")
public ResponseEntity<?> filterUsers(@RequestBody UserFilter userFilter) {
    if(userFilter.getUserId() == null) {
        userFilter.setUserId("*");
    }
    // ... do this for all parameters of userFilter ...
    return ResponseEntity.body(userService.filterUsers(userFilter.getId(), userFilter.getUserFirstName(), userfilter.getUserLastName(), userFilter.getUserEmail()));
}
 

Затем, в хранилище, я бы сделал что-то вроде этого:

 @Query("SELECT u FROM User u WHERE u.id like CONCAT('%', :id, '%') and u.first_name like CONCAT('%', :firstName, '%') and u.last_name like CONCAT('%', :lastName, '%') and u.email like CONCAT('%', :email, '%')")
List<User> filterUsers(@Param("id") Integer id, @Param("firstName") String firstName, @Param("lastName") String lastName, @Param("email") String userEmail); 
 

Для справки по JPQL: https://www.baeldung.com/spring-data-jpa-query