Hi, guys.
Please explain why Hibernate loads entities with batches of power of two elements? How it can be avoided?
To demonstrate this, I made test project with 3 entities: Owner, Car, Manufacturer.
Please see code of entities below:
Owner:Code:
@Entity(name = "com.github.karasik.entity.IOwner")
@Table(name = "OWNER")
@AttributeOverrides(value = {@AttributeOverride(name = "name", column = @Column(name = "OWNER_NAME"))})
public class OwnerDTO extends AbstractNamedEntityDTO implements IOwner {
private Collection<ICar> cars;
@Id
@Column(name = "OWN_AA")
public Integer getIdentifier() {
return super.getIdentifier();
}
@OneToMany(mappedBy = "owner", targetEntity = CarDTO.class, cascade = CascadeType.ALL)
@Override
public Collection<ICar> getCars() {
return cars;
}
@Override
public void setCars(Collection<ICar> cars) {
this.cars = cars;
}
}
Car:Code:
@Entity(name = "com.github.karasik.ICar")
@Table(name = "CAR")
@AttributeOverrides({@AttributeOverride(name = "name", column = @Column(name = "CAR_NAME"))})
public class CarDTO extends AbstractNamedEntityDTO implements ICar {
private IOwner owner;
private IManufacturer manufacturer;
@Id
@Column(name = "CAR_AA")
@Override
public Integer getIdentifier() {
return super.getIdentifier();
}
@Override
@ManyToOne(targetEntity = OwnerDTO.class)
@JoinColumn(name = "CAR_OWN_AA")
public IOwner getOwner() {
return owner;
}
@Override
public void setOwner(IOwner owner) {
this.owner = owner;
}
@ManyToOne(targetEntity = ManufacturerDTO.class)
@JoinColumn(name = "CAR_MAN_AA")
@Fetch(FetchMode.SELECT)
@Override
public IManufacturer getManufacturer() {
return manufacturer;
}
@Override
public void setManufacturer(IManufacturer manufacturer) {
this.manufacturer = manufacturer;
}
}
Manufacturer:Code:
@Entity(name = "com.github.karasik.entity.IManufacturer")
@Table(name = "MANUFACTURER")
@AttributeOverrides({@AttributeOverride(name = "name", column = @Column(name = "MAN_NAME"))})
public class ManufacturerDTO extends AbstractNamedEntityDTO implements IManufacturer {
@Id
@Column(name = "MAN_AA")
@Override
public Integer getIdentifier() {
return super.getIdentifier();
}
}
I have this Database CAR table structure:
Code:
efim=# select * from car;
car_aa | car_name | car_man_aa | car_own_aa
--------+----------+------------+------------
1 | Car 1 | 1 | 1
2 | Car 2 | 2 | 1
3 | Car 3 | 3 | 1
4 | Car 4 | 4 | 1
5 | Car 5 | 5 | 1
6 | Car 6 | 6 | 1
7 | Car 7 | 7 | 1
8 | Car 8 | 8 | 1
9 | Car 9 | 9 | 1
10 | Car 10 | 10 | 1
11 | Car 11 | 11 | 1
(11 rows)
When using Hibernate I load from DB Owner record with own_aa=1 and then initialize Cars collection:
Code:
public Integer getCarCount(Integer manufacturerAa) {
IOwner owner = dao.find(OwnerDTO.class, manufacturerAa);
return owner.getCars().size();
}
Hibernate needs to load 11 car entities.
Please note, it makes two queries to load manufacturers (Fetch=SELECT):
Code:
select
cars0_.CAR_OWN_AA as CAR_OWN_3_2_1_,
cars0_.CAR_AA as CAR_AA1_0_1_,
cars0_.CAR_AA as CAR_AA1_0_0_,
cars0_.CAR_MAN_AA as CAR_MAN_2_0_0_,
cars0_.CAR_OWN_AA as CAR_OWN_3_0_0_
from
CAR cars0_
where
cars0_.CAR_OWN_AA=?
select
manufactur0_.MAN_AA as MAN_AA1_1_0_
from
MANUFACTURER manufactur0_
where
manufactur0_.MAN_AA in (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
select
manufactur0_.MAN_AA as MAN_AA1_1_0_
from
MANUFACTURER manufactur0_
where
manufactur0_.MAN_AA=?
For some reason instead of fetching 11 manufacturers via single query, Hibernate makes two queries.
From source code I can see, that loading is performed in batches of 1,2,...,10,16,32,64,128,...,
hibernate.default_batch_fetch_size entities.
Could you please advice how can I change Hibernate behavior not to perform two queries for 10 and 1 element but to make single query for 11 elements?
I wonder why Hibernate fetching behavior was made this way. An explanation will be very helpful.
Sorry if this question is a duplicate or a well known Hibernate feature.
Thanks a lot!