bwajtr / java-persistence-frameworks-comparison

Comparison of non-JPA SQL mapping frameworks for Java (Jooq, Spring JDBCTemplate, MyBatis, EBean, JDBI, Speedment, sql2o)
MIT License
264 stars 35 forks source link

WRT Ebean ToMany "lazy fetch" and N + 1 #20

Open rbygrave opened 2 years ago

rbygrave commented 2 years ago

Hi,

So there is a comment in the readme about Ebean OneToMany and "lazy fetch" that I'll just respond to a little bit.

Ebean query language allows us to fetch lazy, fetch eagerly, or fetch eagerly with a separate query ... for each path. The important point here around with orm query language design is if there is support for fetch eagerly with a separate query which in Ebean we call fetchQuery. This matters if we care about sql cartesian product &/or honoring max rows in sql (database side max rows vs client side max rows).

That is, an ORM that only has fetch (and not a fetchQuery) will produce sql cartesian product when fetching multiple ToMany paths. It will also perform client side row limiting with a query that has maxRows + fetching a ToMany path.

That is, ORM in general, it is building graphs. As such the paths of the graphs can be cardinality ToOne or ToMany. If we are building a graph with 2 (or more) paths that are ToMany and doing that as a single sql statement this produces a sql cartesian product. Ebean's query language and implementation is specifically designed to handle this (and always honor max rows in sql because that is an important database optimisation).

https://ebean.io/docs/query/fetch https://ebean.io/docs/query/fetch#rules

In comparison to JPQL which only has fetch join - I view that as one of it's major design flaws.

but you can still be hit by the N+1

Lazy loading in Ebean by default is 1 + N/10, but the point is to use the query language to specify the paths to fetch (or automatic query tuning based on profiling).

partial objects

This isn't mentioned but the other major aspect of ORM query language is the question of supporting "partial objects" (only fetching the properties that we need).

e.g. Find customer by id ... with id, name only e.g. Find customer by id ... with id, name + fetch billingAddress e.g. Find customer by id ... with all properties

With Ebean we can specify this using FetchGroup https://ebean.io/docs/query/fetchgroup ... or on the query itself. FetchGroup effectively specifies the "projection of the graph".