2

The CategoryRepository is defined like this:

public interface CategoryRepository extends JpaRepository<Category, Long>{
    @Transactional
    @Query("select c from Category c where name = :name")
    Optional<Category> findByName(String name);
}

Here is the test case:

@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CategoryRepositoryTests {
    @Autowired
    private CategoryRepository categoryRepository;

    @Test
    @Transactional
    public void testCaching(){
        categoryRepository.findByName("cat1");
        categoryRepository.findByName("cat1");
    }
}

In db there is always a category named cat1. My expectation is after the 1st statement

categoryRepository.findByName("cat1");

Category object should be in persistence context and thus the second call shouldn't query the db again. This is not happening. The second call hits the db again. Here are the logs.

9
  • What happens if you don't use custom query but mapping of function name? Commented Apr 12 at 14:57
  • @MilosStojanovic removed @Query still hitting db twice. Commented Apr 12 at 15:10
  • Maybe it's got something to do with transactions. Maybe it resets EntityManager after every transaction (not likely I guess, but that's all that comes up to me). Commented Apr 12 at 15:12
  • @MilosStojanovic is it because the objects are cached based on Id by default? Commented Apr 12 at 15:26
  • 1
    What comes to mind is that since repo method is transactional, in the end of transaction it has to commit data to db and hence in next transaction (which is your second call of repo method) it has to check db again to make sure that data in persistence context is valid (in meaning that it is as is in db). Don't know if all this is true, just an idea. Commented Apr 12 at 17:16

2 Answers 2

0

Hibernate does not try to cache the result of every query executed. Only specific operations like findById benefit from the first-level cache. If you want broader caching, you can either use these methods or implement second-level caching.

In general no transaction using @query will be cached.

Important note: when I say no transactions will be cached, I mean the query itself is not cached and if you run the query again it will query the database again; but the result of your query will be cached even if you use @query. for example in code below the second query which is findById will use the cached cat entity and will not query the database.

public class CategoryRepositoryTests {
    @Autowired
    private CategoryRepository categoryRepository;

    @Test
    @Transactional
    public void testCaching(){
            Category cat = categoryRepository.findByName("cat1");
            categoryRepository.findById(cat.getId()).orElseThrow();
            }
    }
Sign up to request clarification or add additional context in comments.

Comments

0

imagine you changed your test method to:

    @Test
    @Transactional
    public void testCaching(){
        categoryRepository.findByName("cat1"); // nothing found
        Category entity = categoryRepository.findByName("cat2");
        entity.setName("cat1");
        categoryRepository.findByName("cat1");
    }

what should happend? You would want to have that entity returned. This is why default configuration for JPA is to flush the persistence context to the database before a finder ist executed, so the SQL query will pick up that new entity you just created. And the SQL for the finder is always executed on the database.

JPA doesn't have functionality to search the unflushed persistence context containing the in memory entities.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.