Error 1: Using eager fetching
The revelation of Fetchtype.eager has been discussed for several years, and there are many articles explaining it in detail. I wrote an article myself, too. Unfortunately, it is still one of the two most common reasons for performance problems.
Fetchtype defines when hibernate initializes the association. You can specify the fetch attribute using @onetomany, @ManyToOne, @ManyToMany and @onetooneannotation annotations.
@Entity public
class author{
@ManyToMany (mappedby= "authors", Fetch=fetchtype.lazy)
private list< book> books = new Arraylist<book> ();
...
}
When hibernate loads an entity, it also loads the acquired associations instantly. For example, when Hibernate loads a author entity, it also extracts the relevant book entities. This requires additional queries for each author, so it often requires dozens of or even hundreds of additional queries.
This approach is very inefficient because Hibernate does this regardless of whether you want to use associations. It is better to use Fetchtype.lazy instead. It delays the initialization of the relationship until it is used in the business code. This avoids a large number of unnecessary queries and improves the performance of the application.
Fortunately, the JPA specification defines fetchtype.lazy as the default value for all multiple associations. So, you just have to make sure you don't change the default value. But unfortunately, a one-off relationship is not the case. error 2: Ignore default fetchtype for one-to-one associations
Next, to prevent immediate crawling (eager fetching), what you need to do is to change the default fetchtype for all one-to-one associations. Unfortunately, these relationships are immediately crawled by default. In some use cases, that's not a big problem, because you just load an extra database record. However, if you load multiple entities and each entity specifies several such associations, it will quickly add up, stones.
Therefore, it is best to ensure that all one-to-one association settings fetchtype to lazy.
@Entity public
class Review {
@ManyToOne (fetch = Fetchtype.lazy)
@JoinColumn (name = "Fk_book")
Private book book;
...
}
Error 3: Do not initialize the required association
When you use Fetchtype.lazy for all associations to avoid error 1 and error 2 o'clock, you will find several n+1 selection issues in your code. This problem occurs when Hibernate executes 1 queries to select N entities, and then must execute an additional query for each entity to initialize a deferred fetch association.
Hibernate transparently acquires lazy relationships, so it is difficult to find this problem in your code. You just call the associated getter method, and I don't think we all want hibernate to execute any extra queries.
List<author> authors = Em.createquery ("Select a from Author a", Author.class). Getresultlist ();
for (Author a:authors) {
log.info (a.getfirstname () + "+ a.getlastname () +" wrote "
+ a.getbooks (). Size () +" Books. ");
If you use a development configuration to activate the statistics component of Hibernate and monitor the number of executed SQL statements, the n+1 selection problem is more likely to be discovered.
15:06:48,362 INFO [Org.hibernate.engine.internal.StatisticalLoggingSessionEventListener]-Session Metrics {28925
Nanoseconds spent acquiring 1 JDBC connections;
24726 Nanoseconds spent releasing 1 JDBC connections;
1115946 Nanoseconds spent preparing-JDBC statements;
8974211 nanoseconds spent executing-JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 Nanoseconds spent performing 0 L2C puts;
0 Nanoseconds spent performing 0 l2c hits;
0 Nanoseconds spent performing 0 l2c misses;
20715894 Nanoseconds spent executing 1 flushes (flushing a total of entities and collections); 88175 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections)}
As you can see, the JPQL query and the Getbooks method for each call to the 12 selected author entities resulted in 13 queries. This is more than most developers think, when they see such a simple snippet of code.
If you let hibernate initialize the required associations, you can easily avoid this situation. There are a number of different ways to do this. The simplest method is to add a join FETCH statement to the FROM clause.
Author a = em.createquery (
"Select a from Author a JOIN FETCH a.books WHERE a.id = 1",
author.class). Getsingleresu Lt ();
Error 4: Select More records than required
I'm sure you won't be surprised when I tell you that choosing too many records slows down the app's speed. But I still often find this problem when I analyze the application on the phone.
One reason may be that JPQL does not support the use of the offset and limit keywords in SQL queries. This may not seem to limit the number of records retrieved in the query. But you can do that. You only need to set this information on the query interface, not in the JPQL statement.
I do this in the code snippet below. I first sort the selected author entity by ID, and then tell Hibernate to retrieve the first 5 entities.
List<author> authors = Em.createquery ("Select a from Author a ORDER by a.ID ASC", Author.class)
. Setmaxresults (5 )
. Setfirstresult (0)
. Getresultlist ();
Error 5: Do not use binding parameters
Binding parameters are simple placeholders in a query and provide many performance-independent benefits: They are very easy to use. Hibernate performs the required transformations automatically. Hibernate automatically escapes strings to prevent SQL injection vulnerabilities.
And it can also help you implement a high-performance application.
Most applications execute a large number of identical queries, using only a different set of parameter values in the WHERE clause. The binding parameters allow hibernate and the database to identify and optimize these queries.
You can use named binding parameters in the JPQL statement. Each named parameter begins with a ":" followed by its name. After you have defined the binding parameters in the query, you need to call the Setparameter method on the query interface to set the binding parameter values.
typedquery<author> q = em.createquery (
"Select a from Author a WHERE a.id =: id", author.class);
Q.setparameter ("id", 1L);
Author a = Q.getsingleresult ();
Error 6: Execute all logic in the business code
For Java developers, it is natural to implement all the logic at the business level. We can use the languages, libraries and tools that we are most familiar with.
Sometimes, however, it is better to implement the logic of manipulating large amounts of data in a database. You can do this by invoking a function in a JPQL or SQL query or by using a stored procedure.
Let's take a quick look at how to call a function in a JPQL query. If you want to delve into this topic, you can read my article about stored procedures.
You can use standard functions in JPQL queries just as you would call them in SQL queries. You simply reference the name of the function, followed by an opening parenthesis, an optional argument list, and a closing parenthesis.
Query q = Em.createquery ("Select a, Size (a.books) from Author a GROUP by a.id");
list<object[]> results = q.getresultlist ();
Also, you can invoke database-specific or custom database functions through JPA's function functions.
typedquery<book> q = em.createquery (
"Select B from book b WHERE b.id = function (' Calculate ', 1, 2)",
book.c LASS);
Book B = Q.getsingleresult ();
error 7: No reason to call flush method
This is another common mistake. This error often occurs when a developer calls Entitymanager's Flush method after persisting a new entity or updating an existing entity. This forces hibernate to perform dirty checks on all managed entities and to create and execute SQL statements for all pending INSERT, UPDATE, or delete operations. This slows down the application because it prevents hibernate from using some internal optimizations.
Hibernate stores all managed entities in the persistence context and attempts to delay the execution of the write operations as much as possible. This allows hibernate to merge multiple update operations on the same entity into one SQL UPDATE statement, bind multiple identical SQL statements through the JDBC batch, and avoid executing duplicate SQL statements that return the entities that you have used in the current session.
As a rule of thumb, you should avoid any calls to the Flush method. JPQL Bulk Operations is one of the rare exceptions, which I will explain in error 9. Error 8: Use Hibernate to cope with everything
Hibernate's object-relational mapping and various performance optimizations make the implementation of most crud use cases very simple and efficient. This makes hibernate a great choice for many projects. This does not mean that hibernate is a good solution for all projects.
I discussed this issue in detail in a previous post and video. JPA and hibernate provide good support for most standard crud use cases that create, read, or update some database records. For these use cases, object-relational mapping can greatly increase productivity, and Hibernate's internal optimization provides a superior performance.
However, the results are different when you need to perform very complex queries, implement analysis or report cases, or write to a large number of records. None of this is appropriate for both JPA and hibernate query capabilities and the entity-managed lifecycle.
If these use cases account for only a small part of the application, then you can still use hibernate. In general, however, you should look at other frameworks, such as Jooq or QUERYDSL, which are closer to SQL and can avoid any object-relational mappings. Error 9: Update or delete a huge list of entities individually
As you look at your Java code, it is also acceptable to feel that updating or deleting the entity individually. That's how we treat the object, right.
This is probably the standard way to handle Java objects, but if you need to update a large number of database records, that's not a good way to go. In SQL, you only need to define an update or DELETE statement that affects more than one record at a time. The database will handle these operations very effectively.
Unfortunately, working with JPA and hibernate is not that easy. Each entity has its own life cycle, and if you want to update or delete multiple entities, you first need to load them from the database. Then, on each entity, Hibernate generates the required SQL update or DELETE statement for each entity. Therefore, hibernate does not update 1000 database records with only 1 statements, but at least 1001 statements are executed.
Obviously, it takes more time to execute 1001 statements than just execute 1 statements. Fortunately, you can use JPQL, native SQL, or criteria queries to perform the same actions on JPA and hibernate.
But it has some side effects you should know about. When an update or delete operation is performed in the database, the entity is not used. This provides better performance, but it ignores the entity life cycle at the same time, and hibernate cannot update any caches.
I have a detailed explanation of this in the article "How to use native queries to perform bulk updates".
In short, you should not use any lifecycle listeners and the flush and clear methods on Entitymanager before performing a bulk update. The Flush method forces hibernate to write all pending changes to the database before the Clear method detaches all entities from the current persistence context.
Em.flush ();
Em.clear ();
Query query = em.createquery ("UPDATE book b SET b.price = b.price*1.1");
Query.executeupdate ();
Error 10: Use entities for read-only operations
JPA and Hibernate support a number of different projections. If you want to optimize the performance of your application, then you should use projections. The most obvious reason is that you should only select the data that is needed in the use case.
But that's not the only reason. As I showed in my recent tests, dtos projections are much faster than entities even if you read the same database columns.
Using a constructor expression in a SELECT clause instead of an entity is only a small change. But in my test, the DTO projections is 40% faster than the entity. Of course, the comparison between the two values depends on your use case, and you should not be able to improve performance in such a simple and effective way.
Source: http://www.codeceo.com/article/10-hibernate-mistake-performance.html