Java JPA Criteria Query łączenie zapytań query

0

Cześć ukeiłem taką metodę jak poniżej, w metodzie tej wyszukuje sobie samochody, myślałem że z każdym ifem lista samochodów zostanie okrojona o warunek który postawiłem np brand=bmw, color=black, fuel=diesel miało zwracać czarne bmw w dieslu
a niestety zwraca wszystkie samochody w dieslu z bazy danych. Dzieje się to przez to że query nie jest "aktualizowane" tylko z każdym ifem jest nadpisywane i niestety nie potrafię tego przeskoczyć... :/

@Override
    public List<Car> search(String brand, String model, String type, Integer registrationYear, String color,
                            String fuel, String transmission, String doors, boolean available){
 
        CriteriaBuilder cb=entityManager.getCriteriaBuilder();
        CriteriaQuery<Car> query=cb.createQuery(Car.class);
        Root<Car> car=query.from(Car.class);
        query.select(car);
        if(brand!=null){
            query.where(cb.equal(car.get("brand"), brand));
        }
        if(model!=null){
            query.where(cb.equal(car.get("model"), model));
        }
        if(type!=null){
            query.where(cb.equal(car.get("type"), type));
        }
        if(registrationYear !=null){
            query.where(cb.equal(car.get("registrationNumber"), registrationYear));
        }
        if(color!=null){
            query.where(cb.equal(car.get("color"), color));
        }
        if(fuel!=null){
            query.where(cb.equal(car.get("fuel"), Fuel.stringToFuelEnum(fuel)));
        }
        if(transmission!=null){
            query.where(cb.equal(car.get("transmission"), Transmission.stringToTransmissionEnum(transmission)));
        }
        if(doors!=null){
            query.where(cb.equal(car.get("doors"), doors));
        }
        TypedQuery<Car> query1=entityManager.createQuery(query);
        List<Car> s=query1.getResultList();
        return query1.getResultList();
    }

0

Jak dla mnie to źle wygląda. W przypadku JPA możesz sobie albo zrobić metody w stylu findBy..., które automatycznie ci znajdą określone dane. Jednak przy wielu warunkach to bez sensu i wtedy lepiej dać @Query("SELECT y FROM zzz WHERE ooo") nad metodą.
W @Param dajesz parametry po jakich ma szukać.

    @Query("SELECT c FROM Car c WHERE c.brand=?1 AND c.color>=?2 AND c.fuel=?3")
    List<Car> findByBrandAndColorAndFuel(@Param("brand") String brand, @Param("color") String color, @Param("fuel") String fuel);

Gdybyś chciał zrobić na wszystkie możliwe parametry, to nazywasz metodę np. findByAllParams. Metoda wtedy nie ma dwóch ekranów a zwraca to co chcesz, oczywiście musiałbyś do selekta dopisać pozostałe warunki ;)

0

@.andy: chcę zrobić wyszukiwanie z możliwością zaznaczania i kombinowania wielu filtrów a napisać metody findBy z taką ilością możliwości kombinacji to hardcore, a co do drugiego sposobu jak tu zaimplementować kompilację parametrów? Bo chciałbym wyszukac np samochody w benzynie, albo samochody 3 drzwiowe z dieslu w kolorze czerwonym itp, zapytanie w tym wypadku zmienai się dynamicznie zależnie od tego co użytkownik zaznaczy na frontend

1

Bo tak jak w SQL powinno być jedno where, ale w nim może być kilka warunków łączonych np przez "and".
Przykład:

Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.equal(pet.get(Pet_.name), "Fido")
    .and(cb.equal(pet.get(Pet_.color), "brown");
0

Nie powinno być po prostu

query = query.where(…);

?

0

@Charles_Ray: tak też próbuję że w każdym ifie dodaje nowe predicate a na końcu po prostu wykonuje query.where(brandPredicate, modelPredicate) niestety generowało to taki błąd że gdy nie podałem np modelu to wyskakiwał bład: predicate null expresion obszedłem go tak że na początku inicjuje wszystkie predicate i wykonują one zapytanie czy id samochodu jest wieksze od 0. Metoda chałpnicza lecz działa

@Override
    public List<Car> search(String brand, String model, String type, Integer registrationYear, String color,
                            String fuel, String transmission, String doors, boolean available){
        int var=0;
        CriteriaBuilder cb=entityManager.getCriteriaBuilder();
        CriteriaQuery<Car> query=cb.createQuery(Car.class);
        Root<Car> car=query.from(Car.class);
        query.select(car);
        Predicate brandPredicate=cb.greaterThan(car.get("id"), var);
        Predicate modelPredicate=cb.greaterThan(car.get("id"), var);
        Predicate typePredicate=null;  //TUTAJ MUSZE ZROBIĆ TO CO U GÓRY
        Predicate registrationNumberPredicate=null;
        Predicate colorPredicate=null;
        Predicate fuelPredicate=null;
        Predicate transmissionPredicate=null;
        Predicate doorsPredicate=null;
        Predicate availablePredicate =null;;

        if(brand!=null){
            brandPredicate= cb.equal(car.get("brand"), brand);
        }
        if(model!=null){
            modelPredicate= cb.equal(car.get("model"), model);
        }
        if(type!=null){
            query.where(cb.equal(car.get("type"), type));
        }
        if(registrationYear !=null){
            query.where(cb.equal(car.get("registrationNumber"), registrationYear));
        }
        if(color!=null){
            query.where(cb.equal(car.get("color"), color));
        }
        if(fuel!=null){
            query.where(cb.equal(car.get("fuel"), Fuel.stringToFuelEnum(fuel)));
        }
        if(transmission!=null){
            query.where(cb.equal(car.get("transmission"), Transmission.stringToTransmissionEnum(transmission)));
        }
        if(doors!=null){
            query.where(cb.equal(car.get("doors"), doors));
        }
        query.where(brandPredicate, modelPredicate);

        return entityManager.createQuery(query).getResultList();
    }
0

Na logikę, to brakuje Ci jakiegoś OR/AND, wołanie kilka razy where spowoduje prawdopodobnie nadpisanie warunku. Najpierw zbuduj warunek, a na końcu query.where(zlozony_warunek).

Tu masz pokazane jak łączyć warunki: https://www.baeldung.com/jpa-and-or-criteria-predicates

1

A musisz to robić w JPA? Jak ja byłem dawno temu juniorem i miałem taki case to używałem JDBC i sklejałem stringa z zapytaniem. (nie oceniajcie :D). Coś w tym stylu:

String mySql = select * from cars where 1=1;

if(Objects.nonNull(type)) {
    mySql += "and type = :type";
}

I te ify ubierz sobie w jakąś strategię czy coś. Aby iterować po opcjach a nie kodzić je na chama.

Edit:
Pamiętam że parametr dla metody where w JPA też tak sklejałem jakimś andem jak tego SQLa. Ale za cholerę sobie nie przypomnę jak to wyglądało. Na pewno się da.

0

a queryDsl?

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