


[{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/tags/hibernate/","section":"Tags","summary":"","title":"Hibernate","type":"tags"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/","section":"L10e","summary":"","title":"L10e","type":"page"},{"content":" Previously\u0026hellip; # In the first article we introduced the concept of the Persistence Context and explored some of its quirks using an example.\nWe focused on how it can help us (or not so much) when fetching entities, but how does it actually read and load those entities? And why exactly the repeatable-reads feature exists given the limitations that we saw in the last article?\nLet\u0026rsquo;s check it out!\nHibernate\u0026rsquo;s entity load process # When we request that Hibernate load an entity it checks, in order, the first-level cache, the second-level cache (if enabled), and the datasource.\nAssuming that we are requesting a given entity for the first time we would have a miss on both caches, loading the data directly from the database.\nWhen the data is returned, it comes with two objects: the actual entity and the entity loaded state. The loaded state is the converted JDBC ResultSet to an Object[] and is used to determine which changes were made to the entity before flushing it back to the database. Both the entity and the loaded state are stored in the first-level cache.\nEntity first load Suppose now that we request the same entity using the EntityManager.find method. This will go to the Persistence Context and trigger a cache hit, returning the entity without going to the database\nLoading an entity already present on the PC with .find() If we request the same entity using a JPQL/HQL query, we will have the same load steps, but in this case there is no way for the EntityManager to know that our query gets an entity that is already loaded.\nBecause of that it will request for the object to be loaded even if the result is not persisted in the first-level cache, given that the entity is already there.\nLoading an entity already present on the PC with JPQL The second-level cache stores entities loaded states directly, so if it\u0026rsquo;s enabled we can avoid going to the DB when re-requesting the entity. If not enabled, we undergo the same process as the first time we requested the entity.\nEntity states in the Persistence Context # Inside the Persistence Context we can have an entity in four different states: New Transient, Managed, Removed, or Detached. Each one happens at different stages of the entity lifecycle:\nNew Transient: an entity that the Persistence Context is still not aware it exists, for example a newly created entity that will be inserted into the database; Managed: when an entity is persisted it goes to the Managed state. At this stage the Persistence Context becomes aware of changes made to the entity using the loaded state explored on the prior section. This is used to determine when and how an entity should be flushed to the DB; Removed: when we use remove on a managed entity we change it to the removed state. Entities in this state are also flushed; Detached: when we detach or clear the context entities go to the detached state. These entities are not managed by the persistence context anymore unless they are persisted again; The diagram below represents the relation between each state (found on this great article by Vlad):\nJPA entity state transitions When we attempt to load an entity from the first-level cache what the EntityManager actually looks for are the Managed entities in the Persistence Context. If the entity is not found we load it from the datasource to the first-level cache, setting it as a Managed entity.\nHibernate\u0026rsquo;s repeatable reads # Everything explored above allows Hibernate to have application-level repeatable reads: querying for an entity will always return the same result if said entity is already loaded in the Persistence Context, given that no changes were made in the same transaction.\nThis feature is essential for Hibernate\u0026rsquo;s persistence design, preventing lost updates in concurrent scenarios.\nWith that in mind, repeatable reads can lead to inconsistent results when dealing with long-running transactions.\nAs we saw above, if we make a JPQL/HQL query for a entity that was already loaded in the Persistence Context the cache will be ignored and the entity will be loaded directly from the database. Given that, because of the repeatable-reads feature, the newly loaded entity is ignored and the previously loaded data remains unchanged in the Persistence Context, even if that database snapshot differs from the current loaded state.\nThis is by design, since Hibernate is more focused on consistent writes than reads. The diagram below illustrates a long-running transaction where the entity data is changed during the operation (source).\nApplication level repeatable-read In this case we don\u0026rsquo;t want changes made to the entity during the transaction to get lost because of changes made in another operation.\nIf read-only views with updated, fresh data is the objective it\u0026rsquo;s way more recommended to use SQL projections, since they bypass the first-level cache going directly to the datasource.\nConclusion # Hibernate and JPA are very robust frameworks that facilitate working with databases in Java environments where we have complex relations and operations between entities. Because of that, some of the inner workings of those tools are complex and some of the errors that can come with them are not the clearest.\nWith both this article and the first one, I hope that the specifics of entity loading is clearer and that it helps you to design better code using those frameworks. Happy coding :)!\n","date":"2025-07-15","externalUrl":null,"permalink":"/posts/persistence-context-deep-dive/","section":"Posts","summary":"This article explores how the Persistence Context loads and manages entities fetched from the database, going into the possible pitfalls that comes with it.","title":"Persistence Context Deep Dive","type":"posts"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/tags/spring/","section":"Tags","summary":"","title":"Spring","type":"tags"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":" Context # While working on a project using Spring Data JPA + Hibernate I came across a section that looked something like this:\n// SomeClass.java file @Component public SomeClass { private final CoolJpaRepository coolJpaRepository; private final AnotherClass anotherClass; public SomeClass( final CoolJpaRepository coolJpaRepository, final AnotherClass anotherClass ) { this.coolJpaRepository = coolJpaRepository; this.anotherClass = anotherClass; } @Transactional public void doSomething() { // ... coolJpaRepository.findByCustomField(); anotherClass.doAnotherThing(); // ... } } // AnotherClass.java file @Component public AnotherClass { private final CoolJpaRepository coolJpaRepository; public AnotherClass(final CoolJpaRepository coolJpaRepository) { this.coolJpaRepository = coolJpaRepository; } @Transactional(propagation = Propagation.REQUIRES_NEW) public void doAnotherThing() { // ... coolJpaRepository.findByCustomField(); // ... } } Breaking down the example # First, we have a class called SomeClass that depends on both a JPA repository called CoolJpaRepository and another Spring managed class called AnotherClass.\n@Component public SomeClass { private final CoolJpaRepository coolJpaRepository; private final AnotherClass anotherClass; public SomeClass( final CoolJpaRepository coolJpaRepository, final AnotherClass anotherClass ) { this.coolJpaRepository = coolJpaRepository; this.anotherClass = anotherClass; } // ... } This class has a method that runs within a transaction that calls findByCustomField from the JPA repository and doAnotherThing from AnotherClass.\n@Transactional public void doSomething() { // ... coolJpaRepository.findByCustomField(); anotherClass.doAnotherThing(); // ... } When we look at the AnotherClass class we see that it too depends on CoolJpaRepository.\n@Component public AnotherClass { private final CoolJpaRepository coolJpaRepository; public AnotherClass(final CoolJpaRepository coolJpaRepository) { this.coolJpaRepository = coolJpaRepository; } // ... } And it calls findByCustomField on a new transaction (because of the @Transactional(propagation = Propagation.REQUIRES_NEW)).\n@Transactional(propagation = Propagation.REQUIRES_NEW) public void doAnotherThing() { // ... coolJpaRepository.findByCustomField(); // ... } In essence we have the same repository method call (findByCustomField) on different classes that, even though are running on separate transactions, are parts of the same flow.\nThe problem with this is that every time we execute this flow the query related to the findByCustomField method will be executed two times, this of course presuming that JPA/Hibernate does not have some kind of performance optimization in place\u0026hellip;\nEnter the Persistence Context # The Persistence Context acts as a cache between the application code and the database, for that reason being also known as the first-level cache.\nThis cache is created per transaction boundary and guarantees that, given an identifier, one and only one related entity will be inside of it, ensuring consistent entity changes and enabling repeatable reads (AKA reading from the cache) to work.\nIt implements a cache strategy known as write-behind, which basically means that entity changes are first stored on the cache and, on a second moment, are translated to write operations that are sent in batch to the database.\nWhen using the JPA specification the EntityManager manages the Persistence Context while when working with Hibernate directly we use the Session object.\nDoes this help with our initial problem? # Given that repeatable reads are a feature of the Persistence Context we can use it to our advantage on our problem, right? Sadly, not for now.\nAs explained above, we have a new Persistence Context per transaction boundary and on our example we have two different transactions on the same flow thanks to the @Transactional(propagation = Propagation.REQUIRES_NEW). So lets get rid of it:\n@Component public SomeClass { private final CoolJpaRepository coolJpaRepository; private final AnotherClass anotherClass; public SomeClass( final CoolJpaRepository coolJpaRepository, final AnotherClass anotherClass ) { this.coolJpaRepository = coolJpaRepository; this.anotherClass = anotherClass; } @Transactional public void doSomething() { // ... coolJpaRepository.findByCustomField(); anotherClass.doAnotherThing(); // ... } } @Component public AnotherClass { private final CoolJpaRepository coolJpaRepository; public AnotherClass(final CoolJpaRepository coolJpaRepository) { this.coolJpaRepository = coolJpaRepository; } @Transactional // REMOVED THE PROPAGATION public void doAnotherThing() { // ... coolJpaRepository.findByCustomField(); // ... } } With the code above both doSomething and doAnotherThing will share the same transaction boundary and, as a consequence, the same Persistence Context.\nWith these changes, we might expect the following behavior: the first findByCustomField call would populate the cache, and the second call would trigger a repeatable read instead of hitting the database. In actuality, we will still see two queries being issued to the database when running the example above.\nIt\u0026rsquo;s never that simple\u0026hellip;\nCustom queries and the first-level cache # When dealing with custom JPQL/HQL or native SQL queries Hibernate does not check the first-level cache for entities related to these queries, instead going straight to the second-level cache (if enabled) or to the database.\nThis explains why on our example the method findByCustomField() issues two queries even when both calls happen on the same transactional boundary. Since under the hood Spring Data JPA is generating a JPQL query from the method it does not have the benefit of getting the entity from the Persistence Context, even if this entity is already loaded!\nThe exception to this is when we do not attempt to get entities through queries, but instead using methods like EntityManager.find or Session.load . Both this methods interact directly with the Persistence Context and get the entity through the id associated with it. On Spring Data JPA we can use the findById method to achieve the same thing, since it\u0026rsquo;s built on top of EntityManager.find.\nTo make it more concrete, if we had something like:\n@Component public SomeClass { private final CoolJpaRepository coolJpaRepository; private final AnotherClass anotherClass; public SomeClass( final CoolJpaRepository coolJpaRepository, final AnotherClass anotherClass ) { this.coolJpaRepository = coolJpaRepository; this.anotherClass = anotherClass; } @Transactional public void doSomething() { // ... coolJpaRepository.findById(); // CHANGED TO findById anotherClass.doAnotherThing(); // ... } } @Component public AnotherClass { private final CoolJpaRepository coolJpaRepository; public AnotherClass(final CoolJpaRepository coolJpaRepository) { this.coolJpaRepository = coolJpaRepository; } @Transactional public void doAnotherThing() { // ... coolJpaRepository.findById(); // CHANGED TO findById // ... } } The database would receive the first findById query and save the entity on the first-level cache. On the second findById call the Persistence Context would be checked for existing entities and, since it would find the one we just loaded, no queries would be issued to the database.\nConclusion # As we can see the Persistence Context can help us to decrease reading times by avoiding issuing queries, but only for specific use cases where we directly check the first-level cache.\nIf we need custom queries the loaded entities are ignored and we need a second-level cache to not request data from the database.\nSo why do we have repeatable reads? In the next article I intended to discuss this question, how entities are actually managed on the first-level cache, and the possible risks of having repeatable reads.\nStay tuned \u0026#x1f440;.\n","date":"2025-02-16","externalUrl":null,"permalink":"/posts/persistence-context-by-example/","section":"Posts","summary":"What is the Persistence Context in JPA/Hibernate? In this article we explore it using an example and attempt to understand if we can use it to improve reading times.","title":"Persistence Context by Example","type":"posts"},{"content":"","date":"2025-01-13","externalUrl":null,"permalink":"/pt-br/tags/escrita/","section":"Tags","summary":"","title":"Escrita","type":"tags"},{"content":"","date":"2024-12-30","externalUrl":null,"permalink":"/tags/blog/","section":"Tags","summary":"","title":"Blog","type":"tags"},{"content":" Why build a blog? # Nowadays we have numerous ways of publishing our ideas, be it on Medium, Twitter/X, Instagram, etc. This democratization is great but personally I prefer to have control over what I publish and how it\u0026rsquo;s published.\nAs this article greatly explains, platforms can cease to exist someday, like MySpace or Orkut (for my Brazilian folks) once were the biggest social medias and now are like forgotten relics.\nFor this reason I decided to create a simple blog to post about things that I like while having full control on where and how I store those posts. Beyond that, I thought it would be a cool little project \u0026#x1f642;.\nPre-requisites # Bellow are some pre-requisites that guided my choice of stack for building this blog, using the reasons explored above as a basis:\nSupport for plain text files # In order to store the posts I decided to use text files, since they will be the easiest to port to any platform if needed. This means that whatever stack that I choose should translate easily these files to HTML pages.\nEasy to setup initially # It\u0026rsquo;s not easy to implement from scratch this translation from plain text files to HTML. Beyond that, I\u0026rsquo;m not exactly good at designing pages \u0026#x1f440;.\nFor these reasons the stack should provide an easy initial configuration to get up and running as quickly as possible.\nFlexibility to extend # Even though I want an easy initial setup it\u0026rsquo;s important that the stack offer ways to customize the blog as needed to reflect changes that I may want to do in the future.\nBuilding blocks # With the requisites in mind I decided on the following building blocks to create the blog:\nHugo # Hugo is a static site generator framework built with Go focused on speed and flexibility.\nIt uses templates to organize content and resources into static HTML pages. The content can be written in multiple plain text formats, but for this blog I\u0026rsquo;m using Markdown for its flexibility to work on multiple platforms and accessibility to be read by its own.\nBlowfish # Blowfish is a Hugo theme that offers pre-made templates that are very flexible, having lots of configurations available and even allowing to fully customize pages with custom layouts.\nGithub # I use Github to store everything related to the project, from the content written on Markdown to the code necessary for Hugo.\nCloudflare Pages # Cloudflare Pages is used to serve the static pages.\nAnother offering that I considered was Github Pages, but since I was already using Cloudflare to manage the blog domain it made the most sense to continue on the same ecosystem.\nOne advantage of using this approach is that I can leave the repository as private, which is good to upload posts that are still in draft to the repo.\nHow everything works together # Putting the stack above to work looks something like this:\nWrite the post in an markdown file, using Hugo to live check how it looks as a HTML page; Open a PR and check the preview deployment from Cloud Flare Pages; If everything works as expected, merge the changes to the main branch which will automatically trigger the blog deployment; ","date":"2024-12-30","externalUrl":null,"permalink":"/posts/building-a-blog/","section":"Posts","summary":"On this article I explore why and how I created this blog 🚀","title":"Building a Blog","type":"posts"},{"content":"","date":"2024-12-30","externalUrl":null,"permalink":"/tags/writing/","section":"Tags","summary":"","title":"Writing","type":"tags"},{"content":"","date":"2024-08-08","externalUrl":"https://medium.com/building-inventa/infrastructure-resource-creation-with-backstage-f36ae01633b5","permalink":"","section":"Posts","summary":"This article explores AWS resource creation (using SQS as an example) via the Backstage Software Templates plugin and how it helps application and platform developers deploy code faster, cleaner, and safer.","title":"Infrastructure Resource Creation with Backstage","type":"posts"},{"content":"","date":"2020-10-31","externalUrl":"https://medium.com/@luccalafonte/why-bugs-are-kinda-awesome-4ca2bb7ac048","permalink":"","section":"Posts","summary":"My goal with this article is to show the bright side of bug investigation in software development and how we can make the most of it.","title":"Why bugs are (kinda) awesome","type":"posts"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]