Why does Hibernate @OneToOne execute multiple select queries instead of one?

Алексей Ковальчук :

Here are my entity:

@Entity
public class ProductStateEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    @OneToOne
    @JoinColumn(name = "product_id", nullable = false)
    private ProductEntity product;

    // other fields
}


@Entity
public class ProductEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    // other fields
}

If I make a request this way:

session.get(ProductStateEntity.class, 10);

SQL is formed this way:

SELECT product_states.id, product_states.product_id, products.id, -- other columns
FROM product_states
INNER JOIN products ON product_states.product_id=products.id
WHERE product_states.id=10

So far, so good, using INNER JOIN.


If you make a request in this way:

session.createQuery("from ProductStateEntity where id = :id")
            .setParameter("id", 10)
            .list()

SQL is formed this way:

SELECT product_states.id, product_states.product_id, -- other columns
FROM product_states
WHERE product_states.id=10;

SELECT products.id, -- other columns
FROM products
WHERE products.id=10

In this case, 2 requests are made. First a query is made in product_states, and then in products.


That's not all, now we will make such a request, which receives 4 records of 4 id at once:

session.createQuery("from ProductStateEntity where id in :ids")
            .setParameter("ids", Arrays.asList(10, 11, 12, 13))
            .list();

SQL is formed this way:

SELECT product_states.id, product_states.product_id, -- other columns
FROM product_states
WHERE product_states.id IN (10, 11, 12, 13);

SELECT products.id, -- other columns
FROM products
WHERE products.id=10;

SELECT products.id, -- other columns
FROM products
WHERE products.id=11;

SELECT products.id, -- other columns
FROM products
WHERE products.id=12;

SELECT products.id, -- other columns
FROM products
WHERE products.id=13;

In this case, 5 requests are made. First, a request is made in the product_states, the id of all products is obtained, and then it is done on 1 request to receive each of 4 products.


Add join fetch to the previous query:

session.createQuery("from ProductStateEntity p join fetch p.product where p.id in :ids")
            .setParameter("ids", Arrays.asList(10, 11, 12, 13))
            .list();

SQL is formed this way:

SELECT product_states.id, products.id, product_states.product_id, -- other columns
FROM product_states
INNER JOIN products ON product_states.product_id=products.id
WHERE product_states.id IN (10, 11, 12, 13)

Thus, only 1 request is made with INNER JOIN, which is what I want to achieve.


And so the questions are:

  1. Why do you need to specify join fetch explicitly in createQuery? Could this default behavior be made? After all, a single query with join is better than a lot.
  2. Why, without specifying join fetch, additional select queries are not combined into one with id in (...)? Instead, Hibernate makes selects one at a time. Can this be customized?
m.antkowicz :

n+1 fetching strategy is default one for Hibernate - just because, as stated in documentation

These defaults make sense for most associations in the majority of applications

to change this behaviour globally you can set hibernate.default_batch_fetch_size, and you will find some topics in the internet about how to set proper value and why

And one more thing - it's common opinion that fetch join is a solution for every problem but it's not. We must remember about Cartesian Product Problem.

The fetching strategy depends on how our application works, what's the environment setup (e.g. latency in DB connections), what data model do we use and many many other things. There is no one good solution for everyone, that's why we have many fetching strategies in Hibernate

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=162216&siteId=1