Hibernate provides an option to lazy fetch single-sided (to-one) associations using lazy proxy or bytecode instrumentation. However, both approaches suffer some problems. Byte code instrumentation … requires byte code instrumentation, while lazy proxies can be really problematic regarding type casting and instanceof
check especially in existing systems and framewors. But don’t worry. In few words below I’ll describe a solution I’ve figured out for the existing system to improve performance using lazy to-one overcoming casting problems and not using instrumentation.
We will discuss following simple example of entities hierarchy:
@MappedSuperclass
public class BaseEntity {
// ...
}
@Entity
public class A extends BaseEntity {
// ...
}
@Entity
public class ASub1 extends A {
// ...
}
@Entity
public class ASub2 extends A {
// ...
}
@Entity
public class B extends BaseEntity {
@ManyToOne
protected A source;
public A getSource() {
return this.source;
}
public void setSource(A source) {
this.source = source;
}
}
We have here 4 entity classes (A
, ASub1
, ASub2
and B
) where both ASub1
and ASub2
entities are derived from A
, whilst A
class is also an entity i.e. it introduces its own table. I intentionally skip here all things regarding hierarchy mapping because we want to focus only on @ManyToOne
association.
What is the problem with declarations above? If we do from B
query we will get B.source
property fetched eagerly from the database. Depending on the association fetching strategy we use, it can be done using a join or secondary select. It doesn’t look harmful so far, but we need to consider that B
entity may have many more different @XToOne
relations and A
can entity also have some of them, and then all these referenced entities will have their own @XToOne
and […]. Plus that this whole tree will be fetched eagerly by default. Coming to this conclusion we will notice that in worse case scenario simple from B
query can retrieve dozens of rows from database.
In the system I’ve been optimizing recently one of from B
was getting about 80 rows from database while in usual processing scenario none of these 80 entities were used in the business logic. They were only fetched by Hibernate “in case of” being useful. But they never were. This is why a lot of people I talk with say Hibernate is too heavy. Please refer for example this (a good one) thread on stackoverflow to see similar problem and possible soluions.
The solution for this problem is simply to make the association lazy with this change:
@Entity
public class B extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
protected A source;
public A getSource() {
return this.source;
}
public void setSource(A source) {
this.source = source;
}
}
Now B.source
is never fetched from database unless is explicitly asked by calling getSource()
method. The problem is solve, but now the next problem comes. It’s about how Hibernate accomplishes this feature. Instead of hydrating the object from the database on getSource()
call, it already puts an empty proxy object in B.source
property which will be hydrated only after referencing some of its properties.
I personally consider this behavior a Hibernate flaw existing in this technology since the beginning. They say they need to do it, because they always return pure class instance as an entity (it’s not a proxy object), so they need to proxy the object held in the property instead. But this choice results in a different Hibernate flaw - to decide if an object is dirty or not they need to recusively review all fetched entities on session flush. In fact the performance problem resolved hadn’t exposed itself because of fetching too many objects from the database, but because of too many objects held in the session first level cache. These objects were reviewed on each autoflush request and there was hundreds of objects there. And such huge objects tree was fetched because of eager single-sided associations.
So, what is the exact problem of this (Hibernate) lazy implementation. B.source
is lazy so at the beginning Hibernate won’t know what is the exact class of the object that will be put in this field. I’ve intentionally put A
as a type here - please notice, that in B.source
property we can either have A
, ASub1
or ASub2
object and Hibernate doesn’t know which one exactly before it fetches the data from the database. And it can’t fetch anything in advance - because it’s the lazy proxy. Isn’t it?
In B.source
field Hibernate creates a CGLIB/javaassist proxy object but because it doesn’t know the real class it needs to create, according to the field declaration it creates A
class proxy here. If, for example, our B
object has an instance of ASub1
in source
field here is what will happen in the java code using B.getSource()
(what previously was working OK with eager fetching - possibly in your framework and application having already 500K lines of code):
B b = bRepository.getOneBWithASub1Source();
System.out.println(b.getSource());
// class A_$$_jvstbc9_4f
System.out.println(b.getSource() instanceof ASub1);
// false
ASub1 myASub1 = (ASub1) b.getSource();
// throws ClassCastException
To prevent this problem Hibernate recommends to use bytecode instrumentation. This will accomplish no-proxy scenario described as follows:
“No-proxy” fetching: A single-valued association is fetched when the instance variable is accessed. Compared to proxy fetching, this approach is less lazy; the association is fetched even when only the identifier is accessed. It is also more transparent, since no proxy is visible to the application. This approach requires buildtime bytecode instrumentation and is rarely necessary.
I agree with almost anything written here, apart from “it’s rearely necessary”. It looks if you want to use super class in lazy single-sided association, you will always need to use this instrumentation or you won’t be able to work with your model. Or maybe “rarely” concerns using super classes in model? Hopefully not, because not using base classes in the model will result in a very anemic model.
Anyway, doing the optimization I figured a way to achieve the same a instrumentation, but without using it. The proxied object internally sees itself unwrapped from surrounding CGLIB/javaassist proxy in this
. So, using this
you can access original object and Hibernate will fetch it for you, achieving something similar like with instrumentation (this approach is less lazy; the association is fetched even when only the identifier is accessed). Here is the example solution:
@MappedSuperclass
public class BaseEntity {
public BaseEntity getThis() {
return this;
}
}
@Entity
public class B extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY)
protected A source;
public A getSource() {
return this.source!=null ? (A) this.source.getThis() : null;
}
public void setSource(A source) {
this.source = source;
}
}