Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate generating SQL queries when accessing associated entity's id

I have Hibernate Entities that look something like this (getters and setters left out):

@Entity
public class EntityA {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private EntityB parent;
}

@Entity
public class EntityB extends SuperEntity {
    @OneToMany(mappedBy = "parent")
    @Fetch(FetchMode.SUBSELECT)
    @JoinColumn(name = "parent_id")
    private Set<EntityA> children;
}

@MappedSuperclass
public class SuperEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private long itemId;
}

When I query for EntityA it loads fine, with the parent association being replaced by a Hibernate proxy (as it is Lazy). If I want access to the parent's id I perform the following call:

EntityA entityA = queryForEntityA();
long parentId = entityA.getParent().getItemId();

As I understand that call should NOT make a roundtrip to the database, as the Id is stored in the EntityA table, and the proxy should only return that value. However, in my case this generates a SQL statement which fetches EntityB and only then returns the Id.

How can I investigate the problem? What are some likely causes of this incorrect behaviour?

like image 833
Zecrates Avatar asked Sep 17 '10 15:09

Zecrates


People also ask

How SQL creates query in Hibernate?

For Hibernate Native SQL Query, we use Session. createSQLQuery(String query) to create the SQLQuery object and execute it. For example, if you want to read all the records from Employee table, we can do it through below code. When we execute above code for the data setup we have, it produces following output.

How can we overcome n 1 problem in Hibernate?

Hibernate N+1 issue occurs when you use `FetchType. LAZY` for your entity associations. Hibernate will perform n-additional queries to load lazily fetched objects. To escape this issue use join fetch, batching or sub select.

How does Hibernate Query work?

Hibernate is a Java framework that makes it easier to create database-interactive Java applications. In HQL, instead of a table name, it uses a class name. As a result, it is a query language that is database-independent. Hibernate converts HQL queries into SQL queries, which are then used to perform database actions.

Why do we use addScalar in Hibernate?

In this case, Hibernate uses ResultSetMetadata to find column details and returns the list of Object arrays. But, excessive use of ResultSetMetadata may result in poor performance, and this is where the addScalar() method is useful. By using addScalar() method, we can prevent Hibernate from using ResultSetMetadata.


1 Answers

As I understand that call should NOT make a roundtrip to the database, as the Id is stored in the EntityA table, and the proxy should only return that value.

Use property access type. The behavior you're experiencing is a "limitation" of field access type. Here is how Emmanuel Bernard explained it:

That is unfortunate but expected. That's one of the limitations of field level access. Basically we have no way to know that getId() indeed only go and access the id field. So we need to load the entire object to be safe.

So change your code into:

@Entity
public class EntityA {
    private EntityB parent;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    public EntityB getParent() {
        return parent; 
    }
    ...
}

@MappedSuperclass
public class SuperEntity {
    private long itemId;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    public long getItemId() { 
        return itemId;
    }
    ...
}

Related question

  • Hibernate Annotations - Which is better, field or property access?

References

  • Proxy loaded on getId-call when using annotations on fields
  • proxy getId => why sql is generated !
  • HHH-3718 (if this issue can ever be solved)
like image 112
Pascal Thivent Avatar answered Sep 21 '22 20:09

Pascal Thivent