0

I'm experiencing a strange slowness in my new spring boot application, when I fetch a table for reporting purposes.

This is a simple table, no reference to other tables, and it has 5 columns. Rows are 50k. So, I use the simple findAll() method, which is available in JpaRepository.

When the "destination" result is the entity, the findAll() execution takes 5 minutes. When I setup DTO class projection, or interface projection, the execution takes 1-2 minutes.

I believe this is still too much for that amount of data.

Moreover hibernate statistics provides execution time like 0.5 seconds. What takes the remaining 1-2 minutes to get the data in DTO type?

pillesoft
  • 486
  • 1
  • 6
  • 20
  • you test in developing or production environment? also consider break points if you are debugging – Amir Azizkhani Dec 08 '21 at 13:15
  • Increase the fetch size of your query, however creating about 50k of objects will require some time. Object creation is relatively slow, Also make sure you have proper equals and hashcode implementations for your beans. – M. Deinum Dec 08 '21 at 14:26
  • increasing fetch size didn't help – pillesoft Dec 14 '21 at 14:18

3 Answers3

1

@Benda has given good pointers and It would be helpful to attach jvisualvm and use CPU profiling to see where is the time spent. Since the rows are large in number drivers generally make multiple round trips to fetch large amount of data to avoid out of memory scenario and while they are being loaded by hibernate - it gets accumulated it in fist level cache. I once came across a scenario where the transaction was not marked readonly and so at the end Hibernate was trying to dirty check to see if something changed. Also I would recommend you to integrate Javamelody. It's free, open-source and super easy to setup with Spring Boot ( just include a dependency in build). It can tell you the time spent in each use case with service level details and SQL timings - all with a good visual dashboard.

Shailendra
  • 8,874
  • 2
  • 28
  • 37
0

Certainly the serialization/deserialization process which is cumbersome and need too much memory. You can do many things about that :

  • Increase the memory params of your application. See JVM memory options.
  • Use a java library which can handle serialization faster/better than default JDK system. Jackson is pretty good.
  • Put a cache system in place to store objects and retrieve them faster. You can use Spring @Cacheable annotation, @see https://www.baeldung.com/spring-cache-tutorial, or you can use Google guava libs to get a pretty good cache system.
  • Optimize your datamodel to have faster request/responses. See loading objects with Fetch.EAGER / Fetch.LAZY diffs and how you can do to get an optimized way to go for your application.
  • Use Pagination explained very well by John Thompson. It will be a faster way to retrieve a small set of results and let the users navigate inside them from 10 to 10 or 50 to 50.
  • Use NoSQL databases like Redis or MongoDB to get JSON objects for your frontend : https://www.mongodb.com/compatibility/spring-boot

With Pagination example, you can achieve a fast display of your dataset in your spring boot application :

public interface ProductRepository extends PagingAndSortingRepository<Product, Integer> {

    List<Product> findAllByPrice(double price, Pageable pageable);
}

Starting point code from https://www.baeldung.com/spring-data-jpa-pagination-sorting, with instructions you can follow to get it working pretty well, and it is certainly the best way for you to get things working very well.

BendaThierry.com
  • 2,080
  • 1
  • 15
  • 17
  • Jackson isn't going to help improve the speed of the `findAll`. Using `@Cacheable` to cache entities should also be avoided (use the 2nd level cache for your jpa provider for that). – M. Deinum Dec 08 '21 at 14:25
  • @M.Deinum : the findAll() is fast (0.5 seconds) so it is not where the problem is. So use Jackson to build JSON objects loaded by the User Interface will be faster... if well done. For cacheable objects from JPA it's ok, but I have spoken about this because he can use it for its own DTO classes builded one time, for a long time. It depends if datas are changing very often or not though. – BendaThierry.com Dec 08 '21 at 14:31
  • Again mapping a JDBC result to a DTO wont get faster with Jackson. And cacheable for JPA managed entities is a bad idea as you need to replicate the JPA behavior yourself for managing caches. This is all about mapping the result from JDBC to a DTO **not** about marshalling/serializing in the web layer. – M. Deinum Dec 09 '21 at 07:24
  • First of all I have spoken about cache for DTO only, built one time, serving many times. Nothing more, nothing less. – BendaThierry.com Dec 09 '21 at 20:49
  • After that point, if the OP needs something very fast, serving JSON could be done from a NoSQL database like Redis directly, with JSON objects is certainly the best bet. So no time lost with java mapping related stuff at all. – BendaThierry.com Dec 09 '21 at 20:50
  • And if the OP is bounded to his actual configuration, maybe preloading the data at the application's startup with a background task is possible too. Using the cache system of his choice to serve the data faster without waiting the user interaction to initialize the first dataloading stuff ;) but 5 minutes to wait here is too much time lol ! – BendaThierry.com Dec 09 '21 at 21:07
  • sorry, but none of these proposal applicable in that case. I mentioned it is the query for reporting (export the data to excel). so, cache is not an option, change to NoSQL is useless, since this is an RDBMS – pillesoft Dec 14 '21 at 14:21
  • @pillesoft It depends if the reporting is done for you as an admin or for a functionnality accessible to many people or few people, I have apps where many people can look at reporting features. Strange too it is working well with JdbcTemplate and not so well with JpaRepository. I think you can investigate deeper in the SQL generated to see what it has blocked. Glad to see it works and nevermind if none of suggested paths has not helped you, they will certainly help some other people whom are looking on different solutions. – BendaThierry.com Dec 15 '21 at 11:28
0

Finally the solution for this issue was to change from JPA repository to JdbcTemplate.

I constructed a query, and with a data mapper parameter I could get my list into a DTO type in Java. The speed is unbelievable after those many trials with JPA query. which was 5 mins with JPA, it became 2 seconds with JdbcTemplate.query().

after all i would like to know what is changed from Spring 4.3 to Spring Boot 2.5.5

pillesoft
  • 486
  • 1
  • 6
  • 20
  • Believe it or not the origin of that issue was log4j. In the application, which performed well, fast the repository queries was 2.9.1, but when I upgraded to 2.15 or 2.16 (due to vulnerability issue) the queries became extremely slow. Finally I have dropped log4j package and replaced with logback, and the repository queries are working well, and fast again. – pillesoft Jan 07 '22 at 14:56