javaspring-bootjpacriteria-api

Why doing query.from multiple times returns wrong count?


I recently stumbled upon this weird behaviour which I can't quite explain.

If I do

public Long getAllUsersCount() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Long> query = cb.createQuery(Long.class);

    Root<Userr> root = query.from(Userr.class);         // exp = 3; act = 3             exp = 2; act = 2
    //Root<Userr> root2 = query.from(Userr.class);      // exp = 3; act = 9 = 3^2       exp = 2; act = 6
    //Root<Userr> root3 = query.from(Userr.class);      // exp = 3; act = 27 = 3^3      exp = 2; act = 18

    query.select(cb.count(root));

    //query.where(cb.equal(root.get("name"), "josh"));

    return entityManager.createQuery(query).getSingleResult();
}

I correctly get 3 (EXPected = ACTual).

When I uncomment root2 I get 9 which is 3 squared.

Furthermore, when I also uncomment root3 I get 27 which is 3 cubed.

Now let's comment root1 and root2 again but uncomment the where.

With only one root I correctly get 2 (EXPected = ACTual).

When I uncomment root2 I get 6.

And when I also uncomment root3 I get 18.

This is the data I'm working with

INSERT INTO userr (id, name, email) VALUES
  (1, 'josh', 'josh@a.com'),
  (2, 'josh', 'josh@b.com'),
  (3, 'coby', 'coby@a.com');

It looks like the count increases as I increase the number of query.from.

I know I should use only one root, but I just want to understand why this happens. Do you have any idea?

Also it only happens on the count. A normal select always returns the correct items, no matter how many times I do query.from.


Solution

  • Both @turo comment and @rob-spoor answer helped me understand what's happening. But I wanted to elaborate a bit more to also describe what happens when we add a where clause.

    Doing query.from doesn't overwrite the previouse query.from but it adds a new table to the from using the cross join. For example, starting from 1 root to n roots, this is the equivalent sql query:

    In my example I had a table with 3 rows.

    Nice! But what happens when i add the where clause? In this case the where applies only to the first table. Let's see it in practice:

    But what happens when we do just a simple select and not the count?

    Apparently JPA implicitly applies a DISTINCT on the primary key. That's why, no matter how many roots we have, the result is always the same as if we only had one root.