Hibernate @OneToOne executes multiple queries even with @Fetch(FetchMode.JOIN)
Asked Answered
M

3

12

Consider and Employee and Address relationship. There is a One-to-one mapping between Employee and Address. Following are models:

@Entity
@Table(name = "Address")
public class Address
{
    @Id
    @GeneratedValue
    @Column(name = "addressId")
    private int addressId;

    @Column(name = "city")
    private String city;

    .
    .
}

@Entity
@Table(name = "Employee")
public class Employee
{
    @Id
    @Column(name = "employeeId")
    private int employeeId;

    @Column(name = "name")
    private String name;

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "addressId")
    @Fetch(FetchMode.JOIN)
    private Address address;

    .
    .
}

Now when I execute following HQL query it internally generates two queries. One to fetch Employee and another to fetch Address.

"FROM Employee WHERE id = " + 1

SQL queries generated by Hibernate

Hibernate: select employee0_.employeeId as employeeId0_, employee0_.addressId as addressId0_, employee0_.name as name0_ from Employee employee0_ where employee0_.employeeId=1

Hibernate: select address0_.addressId as addressId1_0_, address0_.city as city1_0_ from Address address0_ where address0_.addressId=?

As I am using @Fetch(FetchMode.JOIN), I was expecting Hibernate to execute only 1 query with a join to fetch Employee and Address data in one go.

Any idea why it is executing two queries and how can I make Hibernate to execute only one query using join?

I am using Hibernate 3.3.0.

Moynahan answered 18/2, 2014 at 11:30 Comment(0)
L
10

Using explicit HQL query "FROM Employee WHERE id = " + 1", Hibernate will not respect the annotated Fetch mode. You would need to specify the join in the HQL query or the fetch mode in the criteria.

Hibernate 3.x ignores the Fetch Mode annotation when you use the Query interface (Session.createQuery) so in this case you have to add an INNER JOIN FETCH clause to the FROM part of your query.

Lyons answered 18/2, 2014 at 15:18 Comment(3)
Thanks for the explanation. Using INNER JOIN FETCH resulted into executing a single query.Moynahan
Where should I write the INNER JOIN FETCH if i m using From Employee queryTorrence
I'm having the same problem reported by @SanketMeghani. And I'm wondering why Hibernate don't respect the annotations in this case. Is there any reasonable explanation for this?Burmaburman
S
0

@OneToOne(fetch = FetchType.EAGER) and @Fetch(FetchMode.JOIN) are same as mentioned here you are eager loading Address that means whenever Employee is ask to fetch Address will also be fetched ,as per this doucment

second select will only be executed when you access the association.

that means query for Address will be executed only when you access it so you should use @Fetch(FetchMode.SELECT) and @OneToOne(fetch = FetchType.LAZY).

Update:

From reference with @Swathi's post FetchMode is always ignored by Hibernate when used Query, it will use select for each individual entity, collection, or join load in that case. This means you have to create join your self

For lazy loading Hibernate will ignore FetchType provided through annotation but if mapping is done through XML e.g.Employee.hbm.xml with some thing like this

 <one-to-one name="address" fetch="join" class="**PACKAGE**.Address"
        constrained="true" ></one-to-one>

then Hibernate will respect lazy loading and only execute first select statement if lazy="false" is set on one-to-one then it will execute both select statements.

Smokeproof answered 18/2, 2014 at 12:27 Comment(1)
What I am wondering is why it is executing two separate queries (1 to load Employee and another to load Address) even though I have specified FetchMode.JOIN? While it can achieve loading Employee and Address using a single join query. Or is there a way I can instruct hibernate to use a join query to load both the models using one query?Moynahan
A
0

The way I was able to do this was by setting the field to lazy load and then creating this specification:

specification = Specification.where(null);
        specification = specification.and((root, query, builder) ->{
                    root.fetch("address");
                   return builder.conjunction();
                }
                );
Alfieri answered 26/8 at 10:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.