Hibernate Extended Bytecode Enhancement

Image by GregoryButler from Pixabay

In Hibernate, there’s a mystical option enableExtendedEnhancement intended for fine-tuning of the bytecode enhancement. This option is not described well in the documentation. This article explains what it is, how to use it, and what benefits it may bring to you.

Project Configuration

To demo the effect of Hibernate’s extended enhancement, we’ll start with a simple entity class Cat that contains two fields: an automatically generated id and a name:

By the way, the full code for the article is available at GitHub — this is a very simple demo project using Java 14, Hibernate 5.4, and JUnit 5.

Dirty Checking

Hibernate can automatically save an attached entity instance on flush if you’ve made any changes to it. By default, it does to by keeping the initial state of the object in memory and comparing it to the actual state before flushing. This process is called dirty checking.

Suppose we want to get an entity from the database, update its field and save it back. Thanks to this dirty checking feature, this is very easy:

We update a field of an entity, but we don’t need to ask Hibernate specifically to save the changes —thanks to dirty checking, Hibernate can determine that a field was updated.

However, keeping the whole initial state of the object in memory is costly, and doing a field-by-field comparison before flushing wastes time. So there’s another mechanism in Hibernate for dirty checking, that takes advantage of Hibernate’s bytecode enhancement.

Bytecode Enhancement

Just by adding the hibernate-enhance-maven-plugin to our project, we enable Hibernate to instrument our entity classes on the bytecode level. The enableDirtyTracking option instructs the plugin to enhances the entities with code that keeps track of dirty fields on the fly:

If we now build the project and check out the compiled Cat.class file, we’ll see that code for the on-the-fly dirty tracking was added by the hibernate-enhance-maven-plugin:

Whenever we set the field value, Hibernate compares it to the current value and notifies the tracker, if needed. This has two benefits over the default mechanism of dirty checking:

  • there’s no need to keep the whole initial state of the entity in memory: the tracker only stores the changed values, and only when they do change;
  • there’s no need to do a deep comparison of the entity on flush: we already know which fields have changed.

However, can the hibernate-enhance-maven-plugin detect all places where we update entity values? If we use only getters and setters (which is the best practice anyway), all is good. But what if for some reason we update the fields directly?

Tricking the Bytecode Enhancement

Now let’s try to trick the bytecode enhancement and update a field using a custom method instead of a setter:

If we now look at the compiled code, we’ll see that the hibernate-enhance-maven-plugin is not so easy to trick — it correctly substitutes a tracking call instead of a direct field assignment:

Now let’s make the field public (you most likely never want to do this in actual code!) and try to modify it from another class:

If we now look at the compiled code, we’ll see that the plugin was not so clever to notice this tricky access, and the SneakyCatService class wasn’t instrumented at all.

This means that if we modify the field directly from another class, it won’t be dirty-tracked by Hibernate, and consequently won’t be persisted.

This is easy to see in the following test:

After reloading the entity from the database, we expect its name to be Buddy, but this test fails, since Hibernate didn’t register a change that bypassed its dirty tracking.

This is a corner case of bytecode modification. You won’t observe this issue if you disable bytecode modification and go back to the default dirty checking, as Hibernate would always do the full entity comparison before the flush.

Extended Enhancement Strikes Back

Now let’s enable the extended enhancement using the enableExtendedEnhancement setting of the plugin:

If we now look at the decompiled code of SneakyCatService, we’ll see the following:

The test now passes. The extended enhancement option instructs hibernate-enhance-maven-plugin to look around and instrument non-entity classes too. This ensures that whenever you try to modify the entity, even in such tricky ways as directly accessing its fields, the dirty tracking mechanism will register that.

Conclusion

To sum up, the enableExtendedEnhancement option allows the Hibernate plugin to enhance not only entity classes but also any class that tries to modify the entity fields directly. This allows for more consistent dirty checking.

It’s worth mentioning that this enableExtendedEnhancement setting is described in the source code of the plugin as something to use at your own risk. You’ll most likely want to avoid accessing entity fields in any way except for getters and setters — try to adhere to the best practices!

I’ve made the complete project available at GitHub, so you could get a working project and experiment with extended bytecode enhancement if you want.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Forketyfork

Software developer @ JetBrains Space. I mostly write about Java and microservice architecture. Occasional rants on software development in general.