Due to its ease of use, Spring Knowledge JPA is an extremely common persistence framework. If builders have one thing to complain about, they often criticize object-relational mapping basically or the chance of efficiency issues. And when you can, in fact, criticize the idea of object-relational mapping, you shouldn’t blame Spring Knowledge JPA to your software’s efficiency issues. As a result of in nearly all instances, that’s not Spring Knowledge JPA’s fault. These issues are often attributable to your software misusing options or avoiding necessary efficiency optimizations.
On this article, I’ll present you the 6 most typical efficiency pitfalls when utilizing Spring Knowledge JPA and the way to keep away from them. Not all of those pitfalls are particular to Spring Knowledge JPA. A few of them are attributable to the underlying JPA implementation, e.g., Hibernate or EclipseLink. For the scope of this text, I count on that you just’re utilizing Spring Knowledge JPA with Hibernate.
Pitfall 1: Don’t monitor database operations throughout growth
Whenever you determine to make use of Spring Knowledge JPA to implement your persistence layer, you run into the first efficiency pitfall earlier than you write your first line of code. And you’ll endure from it till you lastly repair it. However don’t fear. You’ll be able to repair it simply and in any part of your venture.
I’m speaking about monitoring all database operations throughout growth. With out paying shut consideration to all of the SQL statements Spring Knowledge JPA is executing, it’s nearly unimaginable to acknowledge potential efficiency points throughout growth. And that’s as a result of the efficiency influence of any inefficiency is dependent upon the quantity and construction of your knowledge. When utilizing a small check database, these inefficiencies are sometimes hardly recognizable. However that drastically modifications whenever you deploy your software to manufacturing.
If you wish to dive deeper into this subject, I like to recommend studying my article about my beneficial logging configuration for Spring Knowledge JPA. Or watch my Knowledgeable Session on Spring Knowledge JPA efficiency tuning out there within the Persistence Hub. Each get into rather more element than I can do on this a part of this put up. Right here I can solely offer you a fast abstract of what it’s best to do to observe your database operations.
Spring Knowledge JPA solely acts as a small layer on prime of a JPA implementation, e.g., Hibernate. Hibernate is liable for all SQL statements. Resulting from that, it’s additionally the system we have to configure to study all executed database operations.
There are 2 issues it’s best to do to get a superb overview of all of the database operations Hibernate performs:
- set the property spring.jpa.properties.hibernate.generate_statistics in your software.properties file to true and
- activate debug logging for org.hibernate.stat and org.hibernate.SQL.
# Really helpful logging configuration
spring.jpa.properties.hibernate.generate_statistics=true
logging.degree.root=INFO
logging.degree.org.hibernate.stat=DEBUG
logging.degree.org.hibernate.SQL=DEBUG
Let’s use this configuration and execute the code within the following code snippet. ChessPlayer is a straightforward entity class, and playerRepo is a typical JpaRepository supplied by Spring Knowledge JPA.
ChessPlayer participant = new ChessPlayer();
participant.setFirstName("Thorben");
participant.setLastName("Janssen");
playerRepo.save(participant);
In your log file, you may then see a paragraph of Session Metrics that summarize all of the operations Hibernate carried out and the two executed SQL statements.
10:45:18 DEBUG 3720 - – [ main] org.hibernate.SQL : choose nextval ('player_sequence')
10:45:18 DEBUG 3720 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
10:45:18 INFO 3720 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
2885900 nanoseconds spent buying 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
502400 nanoseconds spent making ready 2 JDBC statements;
7977700 nanoseconds spent executing 2 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
21197400 nanoseconds spent executing 1 flushes (flushing a complete of 1 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
Based mostly on this data, you may rapidly verify in case your persistence layer executed any surprising operations and determine if you could take a more in-depth have a look at it.
Pitfall 2: Calling the saveAndFlush() technique to persist updates
One other typical efficiency pitfall is to name the saveAndFlush technique supplied by Spring Knowledge JPA’s JpaRepository to persist an replace of a modified entity object. It prevents your JPA implementation from utilizing inside optimizations and forces pointless flushes of your entire persistence context. And to make it even worse, you don’t must name the saveAndFlush technique to persist an replace.
As I defined in my information to JPA’s lifecycle mannequin, all entity objects have a lifecycle state. If you happen to fetched an entity object from the database or endured a brand new one, it has the lifecycle state managed. Which means your JPA implementation is aware of this object and robotically persists all modifications you carry out on it.
To get the most effective efficiency, it’s best to let your JPA implementation determine when it persists these modifications. By default, it flushes the persistence context earlier than executing a question and committing the transaction. In the course of the flush operation, it checks if any managed entity object has been modified, generates the required SQL UPDATE statements, and executes it.
Let’s take a fast have a look at an instance. The next code will get all ChessPlayer objects from the database and modifications their firstName attribute. As you may see, I don’t name any technique on my playerRepo to persist these modifications. However as you may see within the log output, Hibernate executes an SQL UPDATE assertion for each ChessPlayer object to persist the modified firstName. Hibernate does that robotically for all managed entities which have modified because you fetched them from the database.
Record<ChessPlayer> gamers = playerRepo.findAll();
gamers.forEach(participant -> {
log.data("########################");
log.data("Updating " + participant.getFirstName() + " " + participant.getLastName());
participant.setFirstName("Modified");
});
10:46:20 DEBUG 13868 - – [ main] org.hibernate.SQL : choose chessplaye0_.id as id1_1_, chessplaye0_.birth_date as birth_da2_1_, chessplaye0_.first_name as first_na3_1_, chessplaye0_.last_name as last_nam4_1_, chessplaye0_.model as version5_1_ from chess_player chessplaye0_
10:46:20 DEBUG 13868 - – [ main] o.h.stat.inside.StatisticsImpl : HHH000117: HQL: choose generatedAlias0 from ChessPlayer as generatedAlias0, time: 40ms, rows: 4
10:46:20 INFO 13868 - – [ main] c.t.janssen.spring.knowledge.TestProjections : ########################
10:46:20 INFO 13868 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Updating Magnus Carlsen
10:46:20 INFO 13868 - – [ main] c.t.janssen.spring.knowledge.TestProjections : ########################
10:46:20 INFO 13868 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Updating Jorden van Foreest
10:46:20 INFO 13868 - – [ main] c.t.janssen.spring.knowledge.TestProjections : ########################
10:46:20 INFO 13868 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Updating Anish Giri
10:46:20 INFO 13868 - – [ main] c.t.janssen.spring.knowledge.TestProjections : ########################
10:46:20 INFO 13868 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Updating Fabiano Caruana
10:46:20 TRACE 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing session
10:46:20 DEBUG 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing flush-time cascades
10:46:20 DEBUG 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Soiled checking collections
10:46:20 TRACE 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing entities and processing referenced collections
10:46:20 TRACE 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing unreferenced collections
10:46:20 TRACE 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Scheduling assortment removes/(re)creates/updates
10:46:20 DEBUG 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 insertions, 4 updates, 0 deletions to 4 objects
10:46:20 DEBUG 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 (re)creations, 0 updates, 0 removals to 12 collections
10:46:20 TRACE 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Executing flush
10:46:20 DEBUG 13868 - – [ main] org.hibernate.SQL : replace chess_player set birth_date=?, first_name=?, last_name=?, model=? the place id=? and model=?
10:46:20 DEBUG 13868 - – [ main] org.hibernate.SQL : replace chess_player set birth_date=?, first_name=?, last_name=?, model=? the place id=? and model=?
10:46:20 DEBUG 13868 - – [ main] org.hibernate.SQL : replace chess_player set birth_date=?, first_name=?, last_name=?, model=? the place id=? and model=?
10:46:20 DEBUG 13868 - – [ main] org.hibernate.SQL : replace chess_player set birth_date=?, first_name=?, last_name=?, model=? the place id=? and model=?
10:46:20 TRACE 13868 - – [ main] o.h.e.i.AbstractFlushingEventListener : Put up flush
10:46:20 INFO 13868 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
2796500 nanoseconds spent buying 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
447400 nanoseconds spent making ready 5 JDBC statements;
13539100 nanoseconds spent executing 5 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
48408600 nanoseconds spent executing 1 flushes (flushing a complete of 4 entities and 12 collections);
28500 nanoseconds spent executing 1 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
The log output additionally reveals that Hibernate executed 5 JDBC statements and only one flush operation for 4 entities and 12 collections.
That modifications if I name the saveAndFlush technique after altering the firstName attribute of an entity object. As I defined in a current article, the saveAndFlush technique calls the flush technique on JPA’s EntityManager, which forces a flush operation for your entire persistence context.
Record<ChessPlayer> gamers = playerRepo.findAll();
gamers.forEach(participant -> {
log.data("########################");
log.data("Updating " + participant.getFirstName() + " " + participant.getLastName());
participant.setFirstName("Modified");
playerRepo.saveAndFlush(participant);
});
The log output clearly reveals that the calls of the saveAndFlush technique drastically elevated the quantity of labor Hibernate needed to carry out. As a substitute of 1 flush operation for 4 entities and 12 collections, it now executed 5 flush operations for 20 entities and 60 collections.
10:49:34 DEBUG 38820 - – [ main] org.hibernate.SQL : choose chessplaye0_.id as id1_1_, chessplaye0_.birth_date as birth_da2_1_, chessplaye0_.first_name as first_na3_1_, chessplaye0_.last_name as last_nam4_1_, chessplaye0_.model as version5_1_ from chess_player chessplaye0_
10:49:34 DEBUG 38820 - – [ main] o.h.stat.inside.StatisticsImpl : HHH000117: HQL: choose generatedAlias0 from ChessPlayer as generatedAlias0, time: 50ms, rows: 4
10:49:34 INFO 38820 - – [ main] c.t.janssen.spring.knowledge.TestProjections : ########################
10:49:34 INFO 38820 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Updating Magnus Carlsen
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing session
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing flush-time cascades
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Soiled checking collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing entities and processing referenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing unreferenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Scheduling assortment removes/(re)creates/updates
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 insertions, 1 updates, 0 deletions to 4 objects
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 (re)creations, 0 updates, 0 removals to 12 collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Executing flush
10:49:34 DEBUG 38820 - – [ main] org.hibernate.SQL : replace chess_player set birth_date=?, first_name=?, last_name=?, model=? the place id=? and model=?
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Put up flush
10:49:34 INFO 38820 - – [ main] c.t.janssen.spring.knowledge.TestProjections : ########################
10:49:34 INFO 38820 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Updating Jorden van Foreest
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing session
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing flush-time cascades
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Soiled checking collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing entities and processing referenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing unreferenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Scheduling assortment removes/(re)creates/updates
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 insertions, 1 updates, 0 deletions to 4 objects
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 (re)creations, 0 updates, 0 removals to 12 collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Executing flush
10:49:34 DEBUG 38820 - – [ main] org.hibernate.SQL : replace chess_player set birth_date=?, first_name=?, last_name=?, model=? the place id=? and model=?
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Put up flush
10:49:34 INFO 38820 - – [ main] c.t.janssen.spring.knowledge.TestProjections : ########################
10:49:34 INFO 38820 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Updating Anish Giri
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing session
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing flush-time cascades
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Soiled checking collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing entities and processing referenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing unreferenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Scheduling assortment removes/(re)creates/updates
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 insertions, 1 updates, 0 deletions to 4 objects
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 (re)creations, 0 updates, 0 removals to 12 collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Executing flush
10:49:34 DEBUG 38820 - – [ main] org.hibernate.SQL : replace chess_player set birth_date=?, first_name=?, last_name=?, model=? the place id=? and model=?
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Put up flush
10:49:34 INFO 38820 - – [ main] c.t.janssen.spring.knowledge.TestProjections : ########################
10:49:34 INFO 38820 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Updating Fabiano Caruana
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing session
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing flush-time cascades
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Soiled checking collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing entities and processing referenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing unreferenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Scheduling assortment removes/(re)creates/updates
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 insertions, 1 updates, 0 deletions to 4 objects
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 (re)creations, 0 updates, 0 removals to 12 collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Executing flush
10:49:34 DEBUG 38820 - – [ main] org.hibernate.SQL : replace chess_player set birth_date=?, first_name=?, last_name=?, model=? the place id=? and model=?
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Put up flush
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing session
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing flush-time cascades
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Soiled checking collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushing entities and processing referenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Processing unreferenced collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Scheduling assortment removes/(re)creates/updates
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 insertions, 0 updates, 0 deletions to 4 objects
10:49:34 DEBUG 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Flushed: 0 (re)creations, 0 updates, 0 removals to 12 collections
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Executing flush
10:49:34 TRACE 38820 - – [ main] o.h.e.i.AbstractFlushingEventListener : Put up flush
10:49:34 INFO 38820 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
2944500 nanoseconds spent buying 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
632200 nanoseconds spent making ready 5 JDBC statements;
16237100 nanoseconds spent executing 5 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
74224500 nanoseconds spent executing 5 flushes (flushing a complete of 20 entities and 60 collections);
30800 nanoseconds spent executing 1 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
However although Hibernate carried out 5 instances as many flushes as within the earlier instance, it nonetheless solely executed 5 JDBC statements, and the result’s precisely the identical. That reveals that the 4 further flushes attributable to the saveAndFlush technique solely slowed down the appliance however didn’t present any profit.
So, please don’t name any save technique, particularly not the saveAndFlush technique, to persist a change on a managed entity object.
Pitfall 3: Use the improper technique to generate distinctive main key values
Most persistence layers let the database generate distinctive main key values when persisting new entity objects. That’s a really environment friendly strategy basically. However try to be conversant in 2 widespread efficiency pitfalls.
Utilizing database sequences
In case your database helps sequences, it’s best to use GenerationType.SEQUENCE. Utilizing a database sequence permits Hibernate to separate the era of main key values from the execution of the SQL INSERT assertion and to use additional efficiency optimizations.
And whenever you do this, please additionally make certain to outline a sequence generator.
@Entity
public class ChessPlayer {
@Id
@GeneratedValue(technique = GenerationType.SEQUENCE, generator = "player_gen")
@SequenceGenerator(identify = "player_gen", sequenceName = "player_sequence", allocationSize = 50)
non-public Lengthy id;
...
}
In Hibernate 5, this prompts a efficiency optimization primarily based on the configured allocationSize. By default, it’s set to 50. It tells Hibernate that the sequence will get incremented by 50. Which means it might probably increment the retrieved worth 49 instances earlier than it has to request a brand new one.
So, in the event you’re persisting 10 new ChessPlayer entities, Hibernate doesn’t need to request a brand new worth from the database sequence for each entity.
for (int i=0; i<10; i++) {
ChessPlayer participant = new ChessPlayer();
participant.setFirstName("Participant"+i);
participant.setLastName("Participant"+i);
playerRepo.save(participant);
}
It solely requests 2 values to initialize its id generator earlier than incrementing the retrieved values to get distinctive main keys.
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : choose nextval ('player_sequence')
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : choose nextval ('player_sequence')
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 DEBUG 34680 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:22:33 INFO 34680 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
2636300 nanoseconds spent buying 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
792600 nanoseconds spent making ready 12 JDBC statements;
26535100 nanoseconds spent executing 12 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
48818000 nanoseconds spent executing 1 flushes (flushing a complete of 10 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
Utilizing autoincremented columns
In case your database doesn’t assist sequences, you often need to use an autoincremented column as an alternative. In that case, it’s best to annotate your main key attribute with @GeneratedValue(technique=GenerationType.IDENTITY). That tells Hibernate to make use of the autoincremented column to generate the first key worth.
@Entity
public class ChessPlayer {
@Id
@GeneratedValue(technique = GenerationType.IDENTITY)
non-public Lengthy id;
...
}
However in the event you’re annotating your main key attribute with @GeneratedValue(technique=GeneratonType.AUTO) and use a database that doesn’t assist sequences, Hibernate will use the inefficient TABLE technique as an alternative.
@Entity
public class ChessPlayer {
@Id
@GeneratedValue(technique = GenerationType.AUTO)
non-public Lengthy id;
...
}
The TABLE technique makes use of a database desk and pessimistic locks to generate distinctive main key values. This could trigger extreme scalability points.
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : choose next_val as id_val from hibernate_sequence for replace
11:30:17 DEBUG 3152 - – [ main] org.hibernate.SQL : replace hibernate_sequence set next_val= ? the place next_val=?
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 DEBUG 3152 - – [ main] org.hibernate.SQL : insert into chess_player (birth_date, first_name, last_name, model, id) values (?, ?, ?, ?, ?)
11:30:18 INFO 3152 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
6000000 nanoseconds spent buying 11 JDBC connections;
175900 nanoseconds spent releasing 10 JDBC connections;
13378500 nanoseconds spent making ready 30 JDBC statements;
101236600 nanoseconds spent executing 30 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
49539500 nanoseconds spent executing 1 flushes (flushing a complete of 10 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
So, higher keep away from GenerationType.AUTO and outline your most popular era technique explicitly. And in case your software must assist totally different RDBMS, you may observe the solutions I defined in a earlier article.
Pitfall 4: Use keen fetching
One other typical efficiency pitfall is the fetching of related entities. The JPA specification defines 2 FetchTypes that outline when your persistence supplier shall fetch related entities:
- All associations utilizing FetchType.EAGER need to get instantly initialized when an entity will get fetched from the database. That’s the default for all to-one associations.
- The initialization of all associations utilizing FetchType.LAZY will get delayed till your code accesses the affiliation for the primary time. That’s the default for all to-many associations.
Fetching an affiliation takes time and infrequently requires the execution of further SQL statements. So, it’s best to keep away from fetching any associations you don’t use. You’ll be able to obtain that through the use of the default FetchType for all to-many associations and setting the fetch attribute of all to-one associations to FetchType.LAZY.
@Entity
public class ChessGame {
@ManyToOne(fetch = FetchType.LAZY)
non-public ChessPlayer playerWhite;
@ManyToOne(fetch = FetchType.LAZY)
non-public ChessPlayer playerBlack;
...
}
And after you do this, it’s best to observe the suggestions in Pitfall 5 to effectively fetch your associations when wanted.
Pitfall 5: Don’t specify which affiliation you need to fetch
If you happen to observe my earlier recommendation and use FetchType.LAZY for all associations, you’ll almost certainly run into 2 issues:
- If you happen to attempt to entry a lazily fetched affiliation outdoors the present persistence context, Hibernate throws a LazyInitializationException.
- When your code accesses a lazily fetched affiliation for the first time, your persistence supplier must execute a question to fetch the related data from the database. This typically causes the execution of many further queries and is named the n+1 choose situation.
Fortunately, you may simply repair each issues. You solely must outline which associations you need to initialize when fetching an entity object from the database. Utilizing Spring Knowledge JPA, the simplest means to try this is to annotate your repository technique with @EntityGraph or present your personal question with a JOIN FETCH clause. Let’s take a more in-depth have a look at each choices.
Utilizing an @EntityGraph annotation
An entity graph is a JPA function that lets you outline which associations your persistence supplier shall initialize when fetching an entity from the database. Based mostly on the JPA specification, you may outline it utilizing the @NamedEntityGraph annotation or the EntityGraph API.
Spring Knowledge JPA provides another choice. You’ll be able to annotate a repository technique with @EntityGraph and set a comma-separated listing of attribute names as the worth of its attributePaths attribute. Spring Knowledge JPA makes use of that data to outline an entity graph that tells your persistence supplier to fetch the referenced attributes and provides it to the question.
public interface ChessPlayerRepository extends JpaRepository<ChessPlayer, Lengthy> {
@EntityGraph(attributePaths = "tournaments")
Record<ChessPlayer> findPlayersWithTournamentsBy();
}
Let’s use the findPlayersWithTournamentsBy technique in a easy check case and verify the way it impacts the generated SQL assertion.
Record<ChessPlayer> gamers = playerRepo.findPlayersWithTournamentsBy();
log.data("Record chess gamers and their tournaments");
gamers.forEach(participant -> {log.data(participant.getFirstName() + " " + participant.getLastName()
+ " performed in " + participant.getTournaments().measurement() + " tournaments");});
As you may see within the log output, the SQL question not solely selects all columns mapped by the ChessPlayer entity class. It additionally fetches all columns mapped by the ChessTournament class. Based mostly on this knowledge, Hibernate can instantiate ChessPlayer objects and initialize their tournaments attribute with a Set of ChessTournament entity objects.
14:02:22 DEBUG 23240 - – [ main] org.hibernate.SQL : choose chessplaye0_.id as id1_1_0_, chesstourn2_.id as id1_2_1_, chessplaye0_.birth_date as birth_da2_1_0_, chessplaye0_.first_name as first_na3_1_0_, chessplaye0_.last_name as last_nam4_1_0_, chessplaye0_.model as version5_1_0_, chesstourn2_.end_date as end_date2_2_1_, chesstourn2_.identify as name3_2_1_, chesstourn2_.start_date as start_da4_2_1_, chesstourn2_.model as version5_2_1_, tournament1_.players_id as players_2_4_0__, tournament1_.tournaments_id as tourname1_4_0__ from chess_player chessplaye0_ left outer be part of chess_tournament_players tournament1_ on chessplaye0_.id=tournament1_.players_id left outer be part of chess_tournament chesstourn2_ on tournament1_.tournaments_id=chesstourn2_.id
14:02:22 DEBUG 23240 - – [ main] o.h.stat.inside.StatisticsImpl : HHH000117: HQL: choose generatedAlias0 from ChessPlayer as generatedAlias0, time: 49ms, rows: 4
14:02:22 INFO 23240 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Record chess gamers and their tournaments
14:02:22 INFO 23240 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Magnus Carlsen performed in 1 tournaments
14:02:22 INFO 23240 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Jorden van Foreest performed in 1 tournaments
14:02:22 INFO 23240 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Anish Giri performed in 1 tournaments
14:02:22 INFO 23240 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Fabiano Caruana performed in 1 tournaments
14:02:22 INFO 23240 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
2615500 nanoseconds spent buying 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
257900 nanoseconds spent making ready 1 JDBC statements;
4431900 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
23523900 nanoseconds spent executing 1 flushes (flushing a complete of 5 entities and 14 collections);
27900 nanoseconds spent executing 1 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
The ChessPlayer objects and the affiliation to the tournaments at the moment are totally initialized, managed entity objects. You should use them in the identical means as some other entity object.
Utilizing a JOIN FETCH clause
You’ll be able to obtain the identical by annotating your repository technique with @Question and offering a JPQL question with a JOIN FETCH or LEFT JOIN FETCH clause.
public interface ChessPlayerRepository extends JpaRepository<ChessPlayer, Lengthy> {
@Question("SELECT p FROM ChessPlayer p LEFT JOIN FETCH p.tournaments")
Record<ChessPlayer> findPlayersWithJoinedTournamentsBy();
}
A JOIN FETCH clause tells your persistence supplier to fetch the affiliation. The principle distinction to the beforehand mentioned @EntityGraph annotation is that the JOIN FETCH clause is a part of the question assertion. Resulting from that, you may’t use it with a derived question.
Let’s name the findPlayersWithJoinedTournamentsBy technique in the identical check case as we used within the earlier instance.
Record<ChessPlayer> gamers = playerRepo.findPlayersWithJoinedTournamentsBy();
log.data("Record chess gamers and their tournaments");
gamers.forEach(participant -> {log.data(participant.getFirstName() + " " + participant.getLastName()
+ " performed in " + participant.getTournaments().measurement() + " tournaments");});
As you may see within the log output, Hibernate generated the identical SQL question as within the @EntityGraph instance. It fetches all columns mapped by the ChessPlayer and ChessTournament entity courses and joins the corresponding database tables.
14:10:28 DEBUG 37224 - – [ main] org.hibernate.SQL : choose chessplaye0_.id as id1_1_0_, chesstourn2_.id as id1_2_1_, chessplaye0_.birth_date as birth_da2_1_0_, chessplaye0_.first_name as first_na3_1_0_, chessplaye0_.last_name as last_nam4_1_0_, chessplaye0_.model as version5_1_0_, chesstourn2_.end_date as end_date2_2_1_, chesstourn2_.identify as name3_2_1_, chesstourn2_.start_date as start_da4_2_1_, chesstourn2_.model as version5_2_1_, tournament1_.players_id as players_2_4_0__, tournament1_.tournaments_id as tourname1_4_0__ from chess_player chessplaye0_ left outer be part of chess_tournament_players tournament1_ on chessplaye0_.id=tournament1_.players_id left outer be part of chess_tournament chesstourn2_ on tournament1_.tournaments_id=chesstourn2_.id
14:10:28 DEBUG 37224 - – [ main] o.h.stat.inside.StatisticsImpl : HHH000117: HQL: SELECT p FROM ChessPlayer p LEFT JOIN FETCH p.tournaments, time: 40ms, rows: 4
14:10:28 INFO 37224 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Record chess gamers and their tournaments
14:10:28 INFO 37224 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Magnus Carlsen performed in 1 tournaments
14:10:28 INFO 37224 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Jorden van Foreest performed in 1 tournaments
14:10:28 INFO 37224 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Anish Giri performed in 1 tournaments
14:10:28 INFO 37224 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Fabiano Caruana performed in 1 tournaments
14:10:28 INFO 37224 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
2188400 nanoseconds spent buying 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
276000 nanoseconds spent making ready 1 JDBC statements;
3880300 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
18662200 nanoseconds spent executing 1 flushes (flushing a complete of 5 entities and 14 collections);
25600 nanoseconds spent executing 1 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
Pitfall 6: Utilizing advanced interface projections
If you happen to don’t need to change the information you fetched from the database, it’s best to use a DTO as an alternative of an entity projection. That lets you solely fetch the mandatory data and avoids the entity’s managed lifecycle overhead.
The most well-liked means to try this utilizing Spring Knowledge JPA is to make use of an interface projection. You solely must outline an interface with a set of getter strategies. Spring Knowledge JPA then generates an implementation of that interface, creates a question that solely selects the required entity attributes, and maps every row of the question end result to an object of the generated interface implementation.
When utilizing this type of projection, it’s best to solely embody primary attributes. As a result of as quickly as you embody an attribute that represents an affiliation or use Spring’s Expression Language, you’re shedding the efficiency advantages of a DTO projection. As a substitute of producing a question that solely selects the required entity attributes, Spring Knowledge JPA then generates a question that selects your entire entity object. Based mostly on the entity projection, it then gives you an interface illustration that solely offers you entry to the outlined getter strategies. So, in the long run, you get an entity projection’s complete efficiency overhead and a DTO projection’s limitations.
Let’s check out an instance. The TournamentIntf interface defines a getter technique for the match’s identify and Record of PlayerNameIntf interfaces.
public interface TournamentIntf {
String getName();
Record<PlayerNameIntf> getPlayers();
}
And every occasion of the PlayerNameIntf interface represents a participant’s first and final identify.
public interface PlayerNameIntf {
String getFirstName();
String getLastName();
}
Based mostly on these interface definitions, you’d count on that the next repository technique solely selects the identify of the match and the primary and final names of all gamers.
public interface ChessTournamentRepository extends JpaRepository<ChessTournament, Lengthy>{
TournamentIntf findByName(String identify);
}
However in the event you execute the next easy check case, you may see within the log output that Spring Knowledge JPA first chosen a ChessTournament entity object after which fetched all related ChessPlayer entities.
TournamentIntf match = tournamentRepo.findByName("Tata Metal Chess Match 2021");
log.data(match.getName());
for (PlayerNameIntf participant : match.getPlayers()) {
log.data(" - " + participant.getLastName() + ", " + participant.getFirstName());
}
15:47:06 DEBUG 38200 - – [ main] org.hibernate.SQL : choose chesstourn0_.id as id1_2_, chesstourn0_.end_date as end_date2_2_, chesstourn0_.identify as name3_2_, chesstourn0_.start_date as start_da4_2_, chesstourn0_.model as version5_2_ from chess_tournament chesstourn0_ the place chesstourn0_.identify=?
15:47:06 DEBUG 38200 - – [ main] o.h.stat.inside.StatisticsImpl : HHH000117: HQL: choose generatedAlias0 from ChessTournament as generatedAlias0 the place generatedAlias0.identify=:param0, time: 48ms, rows: 1
15:47:06 INFO 38200 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Tata Metal Chess Match 2021
15:47:06 DEBUG 38200 - – [ main] org.hibernate.SQL : choose players0_.tournaments_id as tourname1_4_0_, players0_.players_id as players_2_4_0_, chessplaye1_.id as id1_1_1_, chessplaye1_.birth_date as birth_da2_1_1_, chessplaye1_.first_name as first_na3_1_1_, chessplaye1_.last_name as last_nam4_1_1_, chessplaye1_.model as version5_1_1_ from chess_tournament_players players0_ internal be part of chess_player chessplaye1_ on players0_.players_id=chessplaye1_.id the place players0_.tournaments_id=?
15:47:06 INFO 38200 - – [ main] c.t.janssen.spring.knowledge.TestProjections : - Carlsen, Magnus
15:47:06 INFO 38200 - – [ main] c.t.janssen.spring.knowledge.TestProjections : - van Foreest, Jorden
15:47:06 INFO 38200 - – [ main] c.t.janssen.spring.knowledge.TestProjections : - Giri, Anish
15:47:06 INFO 38200 - – [ main] c.t.janssen.spring.knowledge.TestProjections : - Caruana, Fabiano
15:47:06 INFO 38200 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
2701900 nanoseconds spent buying 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
536500 nanoseconds spent making ready 2 JDBC statements;
9898900 nanoseconds spent executing 2 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
24970800 nanoseconds spent executing 1 flushes (flushing a complete of 5 entities and 14 collections);
26400 nanoseconds spent executing 1 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
The easiest way to keep away from that is to make use of a easy DTO definition that doesn’t embody any associations. On this instance, you may outline 2 separate queries. The primary one will get the identify of the ChessTournament, and the second fetches the names of all gamers.
Or, in the event you nonetheless need to hold the Record getPlayers() technique in your TournamentIntf interface, it’s best to observe my recommendation from the earlier part. If you happen to outline a customized question that selects ChessTournament entities and makes use of a JOIN FETCH clause to fetch the related gamers, you get all of the required data with 1 question. And you may nonetheless use TournamentIntf because the return kind of your repository technique. Spring Knowledge JPA will map the chosen ChessTournament objects to the generated implementations of the TournamentIntf interface.
public interface ChessTournamentRepository extends JpaRepository<ChessTournament, Lengthy>{
@Question("SELECT t FROM ChessTournament t LEFT JOIN FETCH t.gamers WHERE t.identify = :identify")
TournamentIntf findByNameWithPlayers(String identify);
}
If you happen to change the earlier check case to name this repository technique, you may see within the log output that Spring Knowledge JPA now solely executed 1 question.
15:58:04 DEBUG 27036 - – [ main] org.hibernate.SQL : choose chesstourn0_.id as id1_2_0_, chessplaye2_.id as id1_1_1_, chesstourn0_.end_date as end_date2_2_0_, chesstourn0_.identify as name3_2_0_, chesstourn0_.start_date as start_da4_2_0_, chesstourn0_.model as version5_2_0_, chessplaye2_.birth_date as birth_da2_1_1_, chessplaye2_.first_name as first_na3_1_1_, chessplaye2_.last_name as last_nam4_1_1_, chessplaye2_.model as version5_1_1_, players1_.tournaments_id as tourname1_4_0__, players1_.players_id as players_2_4_0__ from chess_tournament chesstourn0_ left outer be part of chess_tournament_players players1_ on chesstourn0_.id=players1_.tournaments_id left outer be part of chess_player chessplaye2_ on players1_.players_id=chessplaye2_.id the place chesstourn0_.identify=?
15:58:04 DEBUG 27036 - – [ main] o.h.stat.inside.StatisticsImpl : HHH000117: HQL: SELECT t FROM ChessTournament t LEFT JOIN FETCH t.gamers WHERE t.identify = :identify, time: 65ms, rows: 4
15:58:04 INFO 27036 - – [ main] c.t.janssen.spring.knowledge.TestProjections : Tata Metal Chess Match 2021
15:58:04 INFO 27036 - – [ main] c.t.janssen.spring.knowledge.TestProjections : - van Foreest, Jorden
15:58:04 INFO 27036 - – [ main] c.t.janssen.spring.knowledge.TestProjections : - Carlsen, Magnus
15:58:04 INFO 27036 - – [ main] c.t.janssen.spring.knowledge.TestProjections : - Caruana, Fabiano
15:58:04 INFO 27036 - – [ main] c.t.janssen.spring.knowledge.TestProjections : - Giri, Anish
15:58:04 INFO 27036 - – [ main] i.StatisticalLoggingSessionEventListener : Session Metrics {
3167300 nanoseconds spent buying 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
656400 nanoseconds spent making ready 1 JDBC statements;
6869500 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C places;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
34136400 nanoseconds spent executing 1 flushes (flushing a complete of 5 entities and 14 collections);
36300 nanoseconds spent executing 1 partial-flushes (flushing a complete of 0 entities and 0 collections)
}
Your software’s efficiency nonetheless suffers from choosing too many columns and the administration overhead of the internally used entity projection. However you at the very least prevented executing further queries to fetch the required associations.
Conclusion
Spring Knowledge JPA gained reputation as a result of it makes implementing your persistence layer straightforward. However you continue to want to grasp how Spring Knowledge JPA, your JPA implementation, and your database work. Ignoring that is among the most typical causes for efficiency issues, and it’ll be sure that you run into the efficiency pitfalls defined on this article.
To keep away from most of those pitfalls, it’s best to first activate a superb logging configuration. Hibernate’s statistics offer you an outline of all executed operations, and the logged SQL assertion present you ways you work together along with your database.
When implementing replace operations, it’s best to keep away from calling the saveAndFlush technique supplied by Spring Knowledge JPA’s JpaRepository. And whenever you’re producing main key values for brand spanking new entities, it’s best to choose GenerationType.SEQUENCE and outline a @SequenceGenerator to learn from Hibernate’s efficiency optimization. In case your database doesn’t assist sequences, it’s best to explicitly outline GenerationType.IDENTITY to forestall Hibernate from utilizing the inefficient desk technique.
To get the most effective efficiency when studying entity objects, it’s best to outline the FetchType of all associations as LAZY. In case your use case must traverse an affiliation between your entities, it’s best to inform your persistence supplier to initialize the affiliation when fetching the entity object. To try this, you may add a JOIN FETCH clause to your question or annotate your repository technique with an @EntityGraph annotation.
And in the event you’re utilizing DTO projections to your read-only operations, it’s best to keep away from advanced, interface-based DTO projections that embody associations. They drive Spring Knowledge JPA to make use of an entity projection as an alternative of a DTO projection.