@ManyToOne and @BatchSize
Asked Answered
C

3

9

I found in some old code strange thing (at least for me).

The field which is annotated @ManyToOne is also annotated with @BatchSize.

I always thought that @BatchSize annotation only affects when annotated at class level or on a collection (@OneToMany) and affects pre-fetching when iterating.

But maybe I am wrong and annotating @ManyToOne with @BatchSize affects something. I can't find the answer in the documentation.

Does annotating @ManyToOne with @BatchSize have sense?

Convocation answered 24/10, 2012 at 11:36 Comment(0)
D
6

@ManyToOne associated with @BatchSize could make sense only if the corresponding field is marked as lazy (lazy=true).

Indeed, if the field is not lazy, it's by definition already loaded since the enclosing entity is loaded, so the problem of database calls doesn't apply.

Imagine a Person class who has a collection of ShoesPair element (ShoesPair.class) and within this one is present an owner field marked as lazy (since optional and not really bringing an important information when retrieving a specific pair of shoes).

One wants to iterate through 25 pair of shoes (25 ShoesPair objects) in order to retrieve their owner.

If the owner field (corresponding to one person) is only annotated with @ManyToOne, there would be 25 select to database.

However, if annoted with @BatchSize(size=5), there would be merely 5 calls and so increasing performance.

From the Hibernate documentation, it is precised that batch size does not only apply with collections:

You can also enable batch fetching of collections.

Hibenate mentions especially @OneToMany cases, because these one are applied with fields that are in 90% of cases marked as lazy.

Dichroism answered 24/10, 2012 at 12:1 Comment(7)
If it really works like you wrote that is ok with me. But I couldn't find it in the documentation. There I see only this: "19.1.5. Using batch fetching Using batch fetching, Hibernate can load several uninitialized proxies if one proxy is accessed. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can configure batch fetching: on the class level and the collection level."Pavlov
I mean the fragment : on the class level and the collection level.Pavlov
@Łukasz Rzeszotarski I think that is considered as pretty obvious by the documentation and that's why it's not mentioned. Indeed, the concept applies only on lazy objects. I updated my answer.Dichroism
Maybe obvious maybe not. The documentation should cover all possibilities or just be written in more general way - like you wrote 'the concept applies to lazy objects'. But thanks for the answer I'm gonna accept it, but maybe someone wants to add something yet.Pavlov
But in the documentation, it's explained a similar example with an owner field. This one could be marked as nothing as @ManyToOne (no matters). According to me, your demand is included into this example.Dichroism
I found this not to be true in my case, which matches what @Mohur explains in his comment.Plugboard
It was not clear to me from the response nor the comments where to place the annotation. This answer to a similar question explains it more clearly: https://mcmap.net/q/41377/-batchsize-but-many-round-trips-when-fetching-a-manytoone-associationPlugboard
M
21

I think the question refers to combining @ManyToOne and @BatchSize on the same field, e.g.:

@ManyToOne
@BatchSize(size = 5)
private User owner;

This use case is not supported by Hibernate, at least when using annotations. The only uses of batch fetching mentioned by the documentation are:

  • On collection fields, i.e., @OneToMany or @ManyToMany (but not @ManyToOne)
  • On the entity class to be fetched

E.g.:

@Entity
@BatchSize(size = 5)
public class User {
  ...
}

This latter case enables batching for all relationships of type User, including many-to-one relationships. However, with the annotation on the entity class it is not possible to control the behaviour on a field-by-field basis.

A search through the Hibernate source code for all uses of @BatchSize confirms the lack of support for your usage. From what I see in AnnotationBinder.java, the @BatchSize annotation is only inspected on the entity class and on fields which have some kind of @XxxToMany annotation.

Mohur answered 20/12, 2013 at 12:9 Comment(2)
I find your answer very interesting. It seems to contradict the one accepted by the OP. I'm not checking your research but you have me convinced so I'm upvoting your answer. It would be cool if an expert could settle this.Uitlander
I don't consider myself an "expert", but I can confirm the Entity annotation works.Kaitlinkaitlyn
D
6

@ManyToOne associated with @BatchSize could make sense only if the corresponding field is marked as lazy (lazy=true).

Indeed, if the field is not lazy, it's by definition already loaded since the enclosing entity is loaded, so the problem of database calls doesn't apply.

Imagine a Person class who has a collection of ShoesPair element (ShoesPair.class) and within this one is present an owner field marked as lazy (since optional and not really bringing an important information when retrieving a specific pair of shoes).

One wants to iterate through 25 pair of shoes (25 ShoesPair objects) in order to retrieve their owner.

If the owner field (corresponding to one person) is only annotated with @ManyToOne, there would be 25 select to database.

However, if annoted with @BatchSize(size=5), there would be merely 5 calls and so increasing performance.

From the Hibernate documentation, it is precised that batch size does not only apply with collections:

You can also enable batch fetching of collections.

Hibenate mentions especially @OneToMany cases, because these one are applied with fields that are in 90% of cases marked as lazy.

Dichroism answered 24/10, 2012 at 12:1 Comment(7)
If it really works like you wrote that is ok with me. But I couldn't find it in the documentation. There I see only this: "19.1.5. Using batch fetching Using batch fetching, Hibernate can load several uninitialized proxies if one proxy is accessed. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can configure batch fetching: on the class level and the collection level."Pavlov
I mean the fragment : on the class level and the collection level.Pavlov
@Łukasz Rzeszotarski I think that is considered as pretty obvious by the documentation and that's why it's not mentioned. Indeed, the concept applies only on lazy objects. I updated my answer.Dichroism
Maybe obvious maybe not. The documentation should cover all possibilities or just be written in more general way - like you wrote 'the concept applies to lazy objects'. But thanks for the answer I'm gonna accept it, but maybe someone wants to add something yet.Pavlov
But in the documentation, it's explained a similar example with an owner field. This one could be marked as nothing as @ManyToOne (no matters). According to me, your demand is included into this example.Dichroism
I found this not to be true in my case, which matches what @Mohur explains in his comment.Plugboard
It was not clear to me from the response nor the comments where to place the annotation. This answer to a similar question explains it more clearly: https://mcmap.net/q/41377/-batchsize-but-many-round-trips-when-fetching-a-manytoone-associationPlugboard
T
-1

Solving N+1 query problem with Hibernate

1 Using Criteria queries with fetchMode

Criteria criteria = session.createCriteria(Customer.class); criteria.setFetchMode("contact", FetchMode.EAGER);

2 HOL fetch join

3 @BatchSize

The @BatchSize annotation can be used to define how many identical associations to populate in a single database query. If the session has 100 customers attached to it and the mapping of the 'contact' collection is annotated with @BatchSize of size n. It means that whenever Hibernate needs to populate a lazy contact collection it checks the session and if it has more customers which their contact collections need to be populated it fetches up to n collections.

@OneToMany(mappedBy="customer",cascade=CascadeType.ALL, fetch=FetchType.LAZY)
    @BatchSize(size=25)
    private Set<Contact> contacts = new HashSet<Contact>();
Thordis answered 25/7, 2017 at 12:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.