-1

I have troubles with my JPQL/SQL query which returns me faulty data. Query looks like that:

public interface ReviewRepository extends JpaRepository<Review,Long> {
    @Query(value = "SELECT review,r,cr,c FROM Review review " +
            "JOIN FETCH review.rental r " +
            "JOIN FETCH r.carRentals cr " +
            "JOIN FETCH cr.car c " //+
            //"JOIN FETCH r.userRentals ur " +
            //"JOIN FETCH ur.user u"
    )
    List<Review> getFiveLatestReviewsWithUsersAndCars();

As you can see I am connecting three tables together(In the end I want to connect 5). Entities structure:

@Entity
@Table(name = "car")
public class Car {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    @Column(name="class")
    private String type;
    @Column(name="brand")
    private String brand;
    @Column(name="fuel")
    private String fuel;
    @Column(name="engine")
    private String engine;
    @Column(name="hp")
    private int hp;
    @Column(name="model")
    private String model;
    @Column(name="sitting_places")
    private byte sittingPlaces;
    @Column(name="rent_price")
    private double price;
    @Column(name="deleted")
    private boolean deleted;
    @Column(name="rate")
    private float rate;
    @OneToOne(mappedBy = "car", fetch = FetchType.LAZY)
    private WebContent webContent;
    @OneToMany(mappedBy = "car", fetch = FetchType.LAZY)
    private Set<CarRental> carRentals;

    public Car() {
    }

    public Car(long id, String type, String brand, String fuel,
               String engine, int hp, String model, byte sittingPlaces,
               double price, boolean deleted, float rate, WebContent webContent,
               Set<CarRental> carRentals) {
        this.id = id;
        this.type = type;
        this.brand = brand;
        this.fuel = fuel;
        this.engine = engine;
        this.hp = hp;
        this.model = model;
        this.sittingPlaces = sittingPlaces;
        this.price = price;
        this.deleted = deleted;
        this.rate = rate;
        this.webContent = webContent;
        this.carRentals = carRentals;
    }

    @Override
    public String toString() {
        return  brand.substring(0,1).toUpperCase() + brand.substring(1) + ' ' +
                model.substring(0,1).toUpperCase() + model.substring(1) + " - " +
                " Engine: " + engine +
                " Fuel: " + fuel +
                " Hp:" + hp +
                " Type: " + type +
                " Sitting Places: " + sittingPlaces;
    }

//equal,getters and setters
@Entity
@Table(name = "rental_car")
public class CarRental {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "rental_id")
    private Rental rental;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "car_id")
    private Car car;

    public CarRental() {
    }

    public CarRental(Long id, Rental rental, Car car) {
        this.id = id;
        this.rental = rental;
        this.car = car;
    }
//equal,getters and setters
@Entity
@Table(name = "rental")
public class Rental {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "start_date")
    private LocalDateTime startDate;
    @Column(name = "end_date")
    private LocalDateTime endDate;
    @OneToOne(mappedBy = "rental", fetch = FetchType.LAZY)
    private Review review;
    @OneToMany(mappedBy = "rental", fetch = FetchType.LAZY)
    private Set<UserRental> userRentals;
    @OneToMany(mappedBy = "car", fetch = FetchType.LAZY)
    private Set<CarRental> carRentals;

    public Rental() {
    }

    public Rental(Long id, LocalDateTime startDate, LocalDateTime endDate,
                  Review review, Set<UserRental> userRentals, Set<CarRental> carRentals) {
        this.id = id;
        this.startDate = startDate;
        this.endDate = endDate;
        this.review = review;
        this.userRentals = userRentals;
        this.carRentals = carRentals;
    }
//equal,getters and setters
@Entity
@Table(name = "review")
public class Review {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "content")
    private String content;
    @Column(name = "rate")
    private Float rate;
    @Column(name = "date")
    private LocalDateTime date;
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "rental_id")
    private Rental rental;

    public Review() {
    }

    public Review(Long id, String content, Float rate, LocalDateTime date, Rental rental) {
        this.id = id;
        this.content = content;
        this.rate = rate;
        this.date = date;
        this.rental = rental;
    }
//equal,getters and setters

If I send it to MySQL I receive data like:

MySQL Result

When I using query at the top in the java code I receive:

Java JPQL code result

I have 6 records where 5 are exactly the same. The only thing I noticed is that Car with id=5 is rented 5 times - maybe this is the reason of exactly 5 times the same object.

If I can clarify it better let me know.

Shadow
  • 33,525
  • 10
  • 51
  • 64
KaOKeS
  • 11
  • 2
  • JOIN FETCH? Are you sure that you use MySQL? The Reference Manual does not know about this, [JOIN Syntax](https://dev.mysql.com/doc/refman/8.0/en/join.html) even does not contain the word FETCH... – Akina Jul 27 '22 at 12:26
  • Yep to be more precise I am using JPQL, you are right. – KaOKeS Jul 27 '22 at 12:30
  • Please provide all necessary information in the question in text form. You can create tables and stuff using Markdown. – Jens Schauder Jul 28 '22 at 07:19

1 Answers1

0

From JPA perspective your query:

    @Query(value = "SELECT review,r,cr,c FROM Review review " +
            "JOIN FETCH review.rental r " +
            "JOIN FETCH r.carRentals cr " +
            "JOIN FETCH cr.car c "
    )
    List<Review> getFiveLatestReviewsWithUsersAndCars();

returns quadruplet (Review,Rental,Set,Car), spring-data somehow manages that and strips off unrelated data, but you are getting duplicates, the correct query would be:

    @Query(value = "SELECT review FROM Review review " +
            "LEFT JOIN FETCH review.rental r " +
            "LEFT JOIN FETCH r.carRentals cr " +
            "LEFT JOIN FETCH cr.car c "
    )
    List<Review> getFiveLatestReviewsWithUsersAndCars();
Andrey B. Panfilov
  • 4,324
  • 2
  • 12
  • 18
  • Thank you Andrey for your effort. The result changed after your modyfication. I am getting 11 results right now. 7 of them are unique, but one is repeated 5 times again. For unique objects the CarRental object is empty. This repeated Review object somehow takes Car object into CarRental and it should not. – KaOKeS Jul 27 '22 at 12:54
  • is there a DISTINCT keyword you could use in JPQL? – Tan Yu Hau Sean Jul 27 '22 at 12:55
  • I added DISTINCT keyword then in results there is no duplicates anymore, but still Review with id=2 has 5 cars in CarRental object when the rest have 0 besides 1. – KaOKeS Jul 27 '22 at 12:59
  • @KaOKeS I'm pretty sure you are not providing enough information, moreover, the details you are trying to hide or assuming irrelevant are actually important. – Andrey B. Panfilov Jul 27 '22 at 13:01
  • @Andrey B. Panfilov What informations should I put more to help understand my problem? I did not want to put all entities which for me seems to be not related, but I am new in this and maybe I miss something. – KaOKeS Jul 27 '22 at 13:09
  • https://pasteboard.co/hp1wuF4gxd1Y.png Here is screenshot taken from IDE. – KaOKeS Jul 27 '22 at 13:26
  • @KaOKeS [here](https://stackoverflow.com/questions/30088649/how-to-use-multiple-join-fetch-in-one-jpql-query) [Vlad Mihalcea](https://stackoverflow.com/users/1025118/vlad-mihalcea) gave some clues on the topic (how to fetch collections). In your case I suspect that equals/hashCode implementation is not correct (everything else actually looks OK, except the fact you are using your own SQL query instead of hibernate's one), or you may apply pagination/filtering to query - it makes sense when dealing with collections. – Andrey B. Panfilov Jul 27 '22 at 15:08