Reputation: 6946
I have following tables in DB:
Person
,
Parent
,
GrandParent
Parent - Person
is OneToMany
relation (Person
has parentId
)
GrandParent - Parent
is ManyToMany
relation (grandparent_parent
table)
I created in PersonRepository
which extends JPARepository
:
@Query("SELECT person.uuid FROM Person person JOIN person.parent parent JOIN parent.grandparents grandparent WHERE grandparent.uuid = ?1")
Set<String> findByGrandParentId(final String parentId);
For fetching all ids of Person
which are under given GrandParent
.
This SQL is created:
SELECT
person0_.uuid as col_0_0_
FROM
person person0_
INNER JOIN
parent parent1_
on person0_.parentid=parent1_.uuid
INNER JOIN
grandparent_parent grandaparent_parent2_
on parent1_.uuid=grandaparent_parent2_.parentid
INNER JOIN
grandparent parent3_
on grandaparent_parent2_.grandparentid=parent3_.uuid
WHERE
parent3_.uuid='13906f55-441c-45bd-bef6-8beefa4119c4'
I logged how much time repository needs fetch data, and it took (average) ~400ms to fetch ~400 records. Then I execute the same SQL query on DB and each time query took no more than 50ms.
I know that this generated query is not optimized because we can only join two tables GRANDPARENT_PARENT
and PERSON
, but this is not the problem here, because such query is executed also below 50ms.
Why I have such differences between fetching by repository and fetching in db? Is it possible to fix that?
Upvotes: 4
Views: 12038
Reputation: 1
or if you are using PreparedStatement::executeQuery
call PreparedStatement::setFetchSize
before you execute the query
Upvotes: 0
Reputation: 1259
Had the same problem, expanding the jdbc fetchSize
property resolved the issue for me:
// adding the fetchSize QueryHint for the query
@QueryHints(@QueryHint(name="org.hibernate.fetchSize", value="200"))
@Query("SELECT person.uuid FROM ....")
Set<String> findByGrandParentId(final String parentId);
// or set it globally, e.g., through application.properties
spring.jpa.properties.hibernate.jdbc.fetch_size=200
Through profiling, found that the bottleneck occurred while matching each ResultSet row to Entity object. Seems that each round trip to the DB slow down the overall performance in my case.
From Oracle JDBC Developer's Guide (emphasis mine):
By default, when Oracle JDBC runs a query, it retrieves a result set of 10 rows at a time from the database cursor. This is the default Oracle row fetch size value. You can change the number of rows retrieved with each trip to the database cursor by changing the row fetch size value.
Standard JDBC also enables you to specify the number of rows fetched with each database round-trip for a query, and this number is referred to as the fetch size. In Oracle JDBC, the row-prefetch value is used as the default fetch size in a statement object. Setting the fetch size overrides the row-prefetch setting and affects subsequent queries run through that statement object.
Upvotes: 1
Reputation: 24527
Multiple Possibilities:
1. The generated Query
The generated query looks pretty fine to me. It's exactly the same as the query within your @Query
annotation.
2. SQL Result to Java Object conversion
I don't know how big your tables are, but: Converting SQL Results to Java Objects takes some time. For small tables this could increase query time by 0-5%.
3. Lazy loading
You didn't show the code for your entities. If you have @OneToMany
or @ManyToMany
relations, JPA will by default use Lazy Loading. This can really slow down everything by magnitudes.
4. Latency
If you execute the SQL Query on the same machine where the SQL DB is, but your Java Application communicates over network with the SQL DB, it can result in much slower queries, too.
(5. Wrong kind of DB. Seems like you build an object graph. Maybe have a look at Neo4j ;-)
)
Upvotes: 3