Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate, get foreign id without loading associated entity

Tags:

java

hibernate

Simple example:

mappings:

@Entity
public class City {
    @Id@GeneratedValue
    private Integer id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Country country;
...


@Entity
public class Country {
    @Id@GeneratedValue
    private Integer id;

    private String name;
...

usage:

Query query = session.createQuery("from City");
List<?> cities = query.list();
for (Object cityObj : cities) {
    City city = (City) cityObj;
    System.out.printf("City: %s %s%n", city.getId(), city.getName());
    Country country = city.getCountry();
    System.out.println("Country retrieved");
    Integer countryId = country.getId();
    System.out.printf("Country id: %s%n", countryId);
}

here's output:

Hibernate: select city0_.id as id0_, city0_.country_id as country3_0_, city0_.name as name0_ from City city0_
City: 1 Astana
Country retrieved
Hibernate: select country0_.id as id1_0_, country0_.name as name1_0_ from Country country0_ where country0_.id=?
Country id: 1
City: 2 Almaty
Country retrieved
Country id: 1
City: 3 Omsk
Country retrieved
Hibernate: select country0_.id as id1_0_, country0_.name as name1_0_ from Country country0_ where country0_.id=?
Country id: 2

Now that's a strange behavior. I can get Country object (probably some proxy) and Hibernate didn't issue any additional SQL queries yet. But when I call country.getId() – hibernate issues SQL query to load full country object. It's obvious that Hibernate knows country.id value so I expected that hibernate will just return that id without any additional SQL queries. But it does not.

The problem is - I don't need that entire entity. I only need id and I don't want that separate SQL query (or JOIN query if I set FetchType.EAGER).

like image 938
vbezhenar Avatar asked Dec 26 '14 10:12

vbezhenar


3 Answers

I think you will have have to change your Country entity like below.Add AccessType annotation on the Id field.

@Entity
    public class Country {
        @Id@GeneratedValue@AccessType("property")
        private Integer id;

        private String name;

Faced similar problem,and followed this article:- Accessor Type Annotation

like image 187
dReAmEr Avatar answered Nov 17 '22 16:11

dReAmEr


So correct JPA-only standard solution will be:

@Entity
public class Country {

    @Id @GeneratedValue @Access(PROPERTY)
    private Integer id;
like image 27
vbezhenar Avatar answered Nov 17 '22 17:11

vbezhenar


Just wanted to add to @RE350's answer.

In Hibernate 5.2.15, if you are using CriteriaQuery. Then this can be done easily without the need of the @AccessType annotation.(which has been deprecated in favor of @AttributeAccessor).

final CriteriaBuilder cb = criteriaBuilder();
final CriteriaQuery<City> query = criteriaQuery();

final Root<City> q = query.from(City.class);
final Path<Integer> uid = q.get("country").get("id"); // Note this!!

Note: You can also fetch country like this in which case, it will actually join the table.

final Path<Integer> uid = q.join("country").get("id");

Update:

In the case of a ManyToMany join however, this does not seem to be easy, unless you have an entity to model the ManyToMany table.

like image 21
EFreak Avatar answered Nov 17 '22 16:11

EFreak