Restowa metoda ze szczegółowym zapytaniem

0

Witam serdecznie

Przerabiam sobie materiał z rest api i natrafiłem na zagadnienie, które sprawia mi problem i chciałem zasięgnąć pomocy.
Do tematu który przerabiam stworzyłem bazę danych kilku samochodów, klasę samochód, interfejs CarJpaRepository, które rozszerza się o JpaRepository, jest tez CarController gdzie są odpowiednie metody.
Dodam, że działam z Postmanem i tutaj testuję metody

Moim problemem jest stworzenie metody która zwróci listę samochodów w sposób posortowany po customowym parametrze.
Może to banalny problem dla was, ale jestem tutaj by zapytac bardziej doświadczonych programistów ode mnie.

Póki co wykombinowałem to by w interfejsie stworzyć customowe szczegółowe zapytanie z adnotacją @Query i za pomocą JPQL.
Nie do końca jestem pewny tego co napisałem powyżej.
Prosiłbym o wskazówki co do rozwiązania zagadnienia

Poniżej wrzucam klasę CarDto, CarJpaRepository gdzie testuje sobię metodę findAllByProducerAndModel, klasę CarController gdzie jest metoda getAll


public class CarDto {

    private Long id;
    private String producer;
    private String model;
    private String engineType;
    private Integer displacementCm3;
    private Integer powerHp;
    private Integer torqueNm;

    public Long getId() {
        return id;
    }

    public String getProducer() {
        return producer;
    }

    public String getModel() {
        return model;
    }

    public String getEngineType() {
        return engineType;
    }

    public Integer getDisplacementCm3() {
        return displacementCm3;
    }

    public Integer getPowerHp() {
        return powerHp;
    }

    public Integer getTorqueNm() {
        return torqueNm;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setProducer(String producer) {
        this.producer = producer;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public void setEngineType(String engineType) {
        this.engineType = engineType;
    }

    public void setDisplacementCm3(Integer displacementCm3) {
        this.displacementCm3 = displacementCm3;
    }

    public void setPowerHp(Integer powerHp) {
        this.powerHp = powerHp;
    }

    public void setTorqueNm(Integer torqueNm) {
        this.torqueNm = torqueNm;
    }
}

public interface CarJpaRepository extends JpaRepository<Car, Long> {


    List<Car> findAllByProducerAndModel(String producer, String model);


}

import org.modelmapper.ModelMapper;
import org.springframework.web.bind.annotation.*;
import pl.kurs.springmvcrest2.dto.CarDto;
import pl.kurs.springmvcrest2.model.Car;
import pl.kurs.springmvcrest2.repository.CarJpaRepository;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/cars")
public class CarController {

    private CarJpaRepository carJpaRepository;
    private ModelMapper mapper;

    public CarController(CarJpaRepository carJpaRepository, ModelMapper mapper) {
        this.carJpaRepository = carJpaRepository;
        this.mapper = mapper;
    }

    @GetMapping("/{id}")
    public CarDto getById(@PathVariable(name = "id") long id) {
        Car loadedCar = carJpaRepository.findById(id).orElseGet(() -> new Car());
        return mapper.map(loadedCar, CarDto.class);
    }

    @GetMapping
    public List<CarDto> getAll() {
        return carJpaRepository.findAll()
                .stream()
                .map(x -> mapper.map(x, CarDto.class))
                .collect(Collectors.toList());
    }


    //localhost:8080/cars/customsearch?producer=BMW&model=M3cs
    @GetMapping("/customsearch")
    public List<CarDto> getAll(@RequestParam(value = "producer") String producer,
                            @RequestParam(value = "model") String model) {
        return carJpaRepository.findAllByProducerAndModel(producer, model)
                .stream()
                .map(x -> mapper.map(x, CarDto.class))
                .collect(Collectors.toList());


    }
3

Właśnie odkryłeś czemu JPA jest wysoce bezużyteczne w realnych zastosowaniach.

  1. Wykonanie czegoś bardziej złożonego wymaga napisania od ciebie SQLa (albo JPQLa), szczególnie kiedy to co chcesz wyciągnąć nijak nie przypomina tabel które masz.
  2. Trzeba i tak przemapować to co wychodzi z JPA na to co potrzebujemy

W efekcie zysk jest żaden. Dużo sensowniej jest użyć jakiegoś JDBI, Spring JDBC Template i pisać sqle albo QueryDSL czy jOOQ i stukać do bazy odpowiednim DSLem i opakować to wszystko "biznesowym" repozytorium które zwraca ładne obiekty domenowe (które potrafią się też skonwertować do DTO na potrzeby RESTa).

2

W JPA również jesteś w stanie to osiągnąć - poczytaj o „Criteria API”. Będziesz mógł dynamicznie tworzyć zapytanie na podstawie przesłanych parametrów.

1

Możesz użyć PagingAndSortingRepository

0

@Shalom JDBI i Spring JDBC Template jest jeszcze przede mną:). Dzieki za wyjaśnienie dlaczego to słaby pomysł, ale chciałbym ten temat przejść by później wiedzieć co jest dobre, a co słabe i dlaczego tak jest.
Własna kwerenda, JPQL i dalej wszystko zgrać w CarController

0

Pierwszy wynik z Google daje rozwiązanie Twojego problemu: https://www.baeldung.com/spring-data-jpa-pagination-sorting#sort

Generalnie jak bym miał na produkcji wystawiać dane jako CRUD to bym sobie poszukał jakiegoś frameworka (https://www.baeldung.com/spring-data-rest-intro) do tego lub sięgnął po GraphQL (https://www.baeldung.com/spring-graphql). Generalnie to trochę robota głupiego gdy programista piszę ręcznie kod do pagingu i sortowania.

0

Cześć
Jeśli dobrze rozumiem to rozszerzam CarJpaRepository o PagingAndSortingRepository i dalej tworze customową kwerendę?

0

Rozszeżyłem CarJpaRepository o PagingAndSortingRepository, stworzyłem customową kwerendę, w klasie CarController z mapowałem ale nie zwraca posortowanej listy samochodów.
Mógłby ktoś rzucić okiem na kod i wskazać błędy? z góry dzięki

public interface CarJpaRepository extends JpaRepository<Car, Long>, PagingAndSortingRepository<Car, Long> {


    List<Car> findAllByProducerAndModel(String producer, String model);
    

    @Query("SELECT c FROM Car c WHERE c.torqueNm > :torque")
    List<Car> findByTorqueBiggerThan(Integer torque);

}
}

    @GetMapping("/customsort")
    public List<CarDto> getSort(@RequestParam(value = "torque") Integer torque) {
        return carJpaRepository.findByTorqueBiggerThan(torque)
                .stream()
                .map(x ->mapper.map(x, CarDto.class))
                .collect(Collectors.toList());
    }

0

A czemu nie używasz Pageable pageable? Skąd metoda ma wiedzieć jak podzielić na strony? Obczaj np tu:
https://www.baeldung.com/spring-data-jpa-pagination-sorting

0

ogólnie gdy sprawdzam metodę w Postmanie to zwraca samochody, tylko nie są posortowane;(. Co robię nie tak? Czego brakuje?

A gdzie masz w tym kodzie sortowanie?

0

dzięki panowie za wskazane błędy, spojrzę głębiej w temat i wrócę do Was:)

0

Wracam do was z updatem z rozwiązaniem mojego problemu, może się komuś przyda :).
Poniżej wrzucam metode, która zwróci listę samochodów w sposób posortowany po customowym parametrze.

@GetMapping("/customsort")
    public List<CarDto> getSort(@RequestParam(value = "property") String property) {

        return carJpaRepository.findAll(Sort.by(property))
                .stream()
                .map(x -> mapper.map(x, CarDto.class))
                .collect(Collectors.toList());

Używając Postmana można ją przetestować na różne sposoby.

0

Działam dalej w temacie restowych metod. Obecnie chcęnapisać metodę, która kasuje wszystkie auta w danym zakresie mocy albo momentu obrotowego.
Tylko nie wiem jak do tego podejść :D. Chciałbym usunąć samochody, które mają od 200 km do 400 km.
Może to dla kogoś banał ale proszę o pomoc/ wskazówki :)

@DeleteMapping()
    public List<CarDto> deleteCars() {
        
    }

1 użytkowników online, w tym zalogowanych: 0, gości: 1