Jpa kids performance

In my web application, I have an object with OneToMany relationships with Child. When selecting my objects, I execute the following query:

from Object o 

and then I have to print for each object how many children he has with

 // Foreach object.children.size() 

Assuming the facility has many children (say, 30,000); Is it a waste of resources causing size () or the ORM framework (in my case Hibernate) to take care of this without loading all the children?

+4
source share
3 answers

Using JPA (standard):

  • The default @OneToMany relation is lazy-load (i.e. the default value for fetch = FetchType.LAZY). But calling Entity.getCollection().size() will result in lazy loading to get the entire child collection - so yes, it will be pretty slow if you don't need to work with all / most of the elements. Note: for all (normal) JPA implementations, this will NOT issue 30,000 separate requests - it issues a single request that returns 30,000 rows in the result set.
  • If you need most elements or you want to cache the change in @OneToMany(fetch=FetchType.EAGER) on @OneToMany(fetch=FetchType.EAGER)
  • The general way to get data statistics without retrieving each individual object is to simply use JPQL through EntityManager.getQuery()/getTypedQuery()/getNamedQuery() (or even SQL through getNativeQUery() ). It is quite simple and highly efficient:
 int empSize = em.createQuery("SELECT SIZE(d.employees) FROM Department d") .getSingleResult(); 
 OR ALTERNATIVELY // Pre-compiled query placed against entity class for highest performance @NamedQueries({ @NamedQuery(name="Department.EmployeeSize", query="SELECT SIZE(d.employees) FROM Department"), ... // other named queries }) @Entity public class Department { ... } // Then to use the query: int empSize = em.createNamedQuery("Department.EmployeeSize", Integer.class) .getSingleResult(); 
 Map propertiesMap = new HashMap(); // Valid values are ALL, NONE, ENABLE_SELECTIVE, DISABLE_SELECTIVE propertiesMap.add("javax.persistence.sharedCache.mode", "ENABLE_SELECTIVE"); EntityManagerFactory = Persistence.createEntityManagerFactory("myPUName", propertiesMap); 
 ALTERNATIVELY use persistence.xml: <persistence-unit name="EmployeeService"> ... <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> </persistence-unit> 

Then note which objects should be automatically cached in the level 2 cache:

 @Cacheable(true) @Entity public class Employee { ... } 
  • You can also configure dynamic caching as part of a specific request.

Using a proprietary approach (for example, Hibernate "Extra-Lazy"):

  • Same performance as just issuing a JPQL / SQL query
  • Save a couple lines of code (@ org.hibernate.annotations.LazyCollection (EXTRA) annotation v @NamedQuery annotations and execution)
  • Non-standard - JPA developers will not know about it
  • Not portable - can only be used with this provider. There is an industry trend towards standard JPA, from proprietary features. There are many different JPA implementations.
+3
source

It is always useful to include log messages in your JPA implementation and see which database queries are actually sent to the database. In your case, I would say that ORM will probably lazily load all objects, so it will be very inefficient.

+2
source

Hibernate has a special feature called "extra lazy" collections that will do exactly what you just suggested. The value, if you call size() in one of these extra collections, it will return the number of children in memory if the collection is already initialized, but will query the database for count if the collection has not yet been initialized. The goal is to avoid initializing very large collections until absolutely necessary. Other examples of unnecessary operations include handling Collection.contains / Map.containsKey calls, Map.get calls, List.get(int) calls, etc.

To mark a collection as redundant with annotations, you could say @org.hibernate.annotations.LazyCollection( EXTRA )

+1
source

Source: https://habr.com/ru/post/1443208/


All Articles