What are the pros and cons of using Criteria or HQL? The Criteria API is a nice object-oriented way to express queries in Hibernate, but sometimes Criteria Queries are more difficult to understand/build than HQL.
When do you use Criteria and when HQL? What do you prefer in which use cases? Or is it just a matter of taste?
I mostly prefer Criteria Queries for dynamic queries. For example it is much easier to add some ordering dynamically or leave some parts (e.g. restrictions) out depending on some parameter.
On the other hand I'm using HQL for static and complex queries, because it's much easier to understand/read HQL. Also, HQL is a bit more powerful, I think, e.g. for different join types.
There is a difference in terms of performance between HQL and criteriaQuery, everytime you fire a query using criteriaQuery, it creates a new alias for the table name which does not reflect in the last queried cache for any DB. This leads to an overhead of compiling the generated SQL, taking more time to execute.
Regarding fetching strategies [http://www.hibernate.org/315.html]
Criteria respects the laziness settings in your mappings and guarantees that what you want loaded is loaded. This means one Criteria query might result in several SQL immediate SELECT statements to fetch the subgraph with all non-lazy mapped associations and collections. If you want to change the "how" and even the "what", use setFetchMode() to enable or disable outer join fetching for a particular collection or association. Criteria queries also completely respect the fetching strategy (join vs select vs subselect).
HQL respects the laziness settings in your mappings and guarantees that what you want loaded is loaded. This means one HQL query might result in several SQL immediate SELECT statements to fetch the subgraph with all non-lazy mapped associations and collections. If you want to change the "how" and even the "what", use LEFT JOIN FETCH to enable outer-join fetching for a particular collection or nullable many-to-one or one-to-one association, or JOIN FETCH to enable inner join fetching for a non-nullable many-to-one or one-to-one association. HQL queries do not respect any fetch="join" defined in the mapping document.
Criteria is an object-oriented API, while HQL means string concatenation. That means all of the benefits of object-orientedness apply:
All else being equal, the OO version is somewhat less prone to error. Any old string could get appended into the HQL query, whereas only valid Criteria objects can make it into a Criteria tree. Effectively, the Criteria classes are more constrained.
With auto-complete, the OO is more discoverable (and thus easier to use, for me at least). You don't necessarily need to remember which parts of the query go where; the IDE can help you
You also don't need to remember the particulars of the syntax (like which symbols go where). All you need to know is how to call methods and create objects.
Since HQL is very much like SQL (which most devs know very well already) then these "don't have to remember" arguments don't carry as much weight. If HQL was more different, then this would be more importatnt.
I usually use Criteria when I don't know what the inputs will be used on which pieces of data. Like on a search form where the user can enter any of 1 to 50 items and I don't know what they will be searching for. It is very easy to just append more to the criteria as I go through checking for what the user is searching for. I think it would be a little more troublesome to put an HQL query in that circumstance. HQL is great though when I know exactly what I want.
HQL is much easier to read, easier to debug using tools like the Eclipse Hibernate plugin, and easier to log. Criteria queries are better for building dynamic queries where a lot of the behavior is determined at runtime. If you don't know SQL, I could understand using Criteria queries, but overall I prefer HQL if I know what I want upfront.
Criteria API
Criteria API is better suited for dynamically generated queries. So, if you want to add WHERE clause filters, JOIN clauses, or vary the ORDER BY clause or the projection columns, then the Criteria API can help you generate the query dynamically in a way that also prevents SQL Injection attacks.
On the other hand, Criteria queries are less expressive and can even lead to very complicated and inefficient SQL queries.
JPQL and HQL
JPQL is the JPA standard entity query language while HQL extends JPQL and adds some Hibernate-specific features.
JPQL and HQL are very expressive and resemble SQL. Unlike Criteria API, JPQL and HQL make it easy to predict the underlying SQL query that's generated by the JPA provider. It's also much easier to review one's HQL queries than Criteria ones.
It's worth noting that selecting entities with JPQL or Criteria API makes sense if you need to modify them. Otherwise, a DTO projection is a much better choice.
Conclusion
If you don't need to vary the entity query structure, then use JPQL or HQL. If you need to change the filtering or sorting criteria or change the projection, then use Criteria API.
However, just because you are using JPA or Hibernate, it doesn't mean you should not use native SQL. SQL queries are very useful and JPQL and Criteria API are not a replacement for SQL.
Criteria are the only way to specify natural key lookups that take advantage of the special optimization in the second level query cache. HQL does not have any way to specify the necessary hint.
You can find some more info here:
http://tech.puredanger.com/2009/07/10/hibernate-query-cache/
Criteria Api is one of the good concept of Hibernate. according to my view these are the few point by which we can make difference between HQL and Criteria Api
HQL is to perform both select and non-select operations on the data, but Criteria is only for selecting the data, we cannot perform non-select operations using criteria.
HQL is suitable for executing Static Queries, where as Criteria is suitable for executing Dynamic Queries
HQL doesn’t support pagination concept, but we can achieve pagination with Criteria.
Criteria used to take more time to execute than HQL.
With Criteria we are safe with SQL Injection because of its dynamic query generation but in HQL as your queries are either fixed or parametrized, there is no safe from SQL Injection
To use the best of both worlds, the expressivity and conciseness of HQL and the dynamic nature of Criteria consider using Querydsl.
Querydsl supports JPA/Hibernate, JDO, SQL and Collections.
I am the maintainer of Querydsl, so this answer is biased.
For me Criteria is a quite easy to Understand and making Dynamic queries. But the flaw i say so far is that It loads all many-one etc relations because we have only three types of FetchModes i.e Select, Proxy and Default and in all these cases it loads many-one (may be i am wrong if so help me out :))
2nd issue with Criteria is that it loads complete object i.e if i want to just load EmpName of an employee it wont come up with this insted it come up with complete Employee object and i can get EmpName from it due to this it really work bad in reporting. where as HQL just load(did't load association/relations) what u want so increase performance many times.
One feature of Criteria is that it will safe u from SQL Injection because of its dynamic query generation where as in HQL as ur queries are either fixed or parameterised so are not safe from SQL Injection.
Also if you write HQL in ur aspx.cs files, then you are tightly coupled with ur DAL.
Overall my conclusion is that there are places where u can't live without HQL like reports so use them else Criteria is more easy to manage.
For me the biggest win on Criteria is the Example API, where you can pass an object and hibernate will build a query based on those object properties.
Besides that, the criteria API has its quirks (I believe the hibernate team is reworking the api), like:
a criteria.createAlias("obj") forces a inner join instead of a possible outer join
you can't create the same alias two times
some sql clauses have no simple criteria counterpart (like a subselect)
etc.
I tend to use HQL when I want queries similar to sql (delete from Users where status='blocked'), and I tend to use criteria when I don't want to use string appending.
Another advantage of HQL is that you can define all your queries before hand, and even externalise them to a file or so.
Criteria api provide one distinct feature that Neither SQL or HQL provides. ie. it allows compile time checking of a query.
We used mainly Criteria in our application in the beginning but after it was replaced with HQL due to the performance issues.
Mainly we are using very complex queries with several joins which leads to multiple queries in Criteria but is very optimized in HQL.
The case is that we use just several propeties on specific object and not complete objects. With Criteria the problem was also string concatenation.
Let say if you need to display name and surname of the user in HQL it is quite easy (name || ' ' || surname) but in Crteria this is not possible.
To overcome this we used ResultTransormers, where there were methods where such concatenation was implemented for needed result.
Today we mainly use HQL like this:
String hql = "select " +
"c.uuid as uuid," +
"c.name as name," +
"c.objective as objective," +
"c.startDate as startDate," +
"c.endDate as endDate," +
"c.description as description," +
"s.status as status," +
"t.type as type " +
"from " + Campaign.class.getName() + " c " +
"left join c.type t " +
"left join c.status s";
Query query = hibernateTemplate.getSessionFactory().getCurrentSession().getSession(EntityMode.MAP).createQuery(hql);
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
return query.list();
so in our case the returned records are maps of needed properties.
Criteria query for dynamically we can construct query based on our inputs..In case of Hql query is the static query once we construct we can't change the structure of the query.
HQL is to perform both select and non-select operations on the data, but Criteria is only for selecting the data, we cannot perform non-select operations using criteria
HQL is suitable for executing Static Queries, where as Criteria is suitable for executing Dynamic Queries
HQL doesn’t support pagination concept, but we can achieve pagination with Criteria
Criteria used to take more time to execute then HQL
With Criteria we are safe with SQL Injection because of its dynamic query generation but in HQL as your queries are either fixed or parametrized, there is no safe from SQL Injection.
source
I don't want to kick a dead horse here, but it is important to mention that Criteria queries are now deprecated. Use HQL.
I also prefer Criteria Queries for dynamic queries. But I prefer hql for delete queries, for example if delete all records from child table for parent id 'xyz', It is easily achieved by HQL, but for criteria API first we must fire n number of delete query where n is number of child table records.
Most the answers here are misleading and mention that Criteria Queries are slower than HQL, which is actually not the case.
If you delve deep and perform some tests you will see Criteria Queries perform much better that regular HQL.
And also with Criteria Query you get Object Oriented control which is not there with HQL.
For more information read this answer here.
There is another way. I ended up with creating a HQL parser based on hibernate original syntax so it first parse the HQL then it could dynamically inject dynamic parameters or automatically adding some common filters for the HQL queries. It works great!
This post is quite old. Most answers talk about Hibernate criteria, not JPA criteria. JPA 2.1 added CriteriaDelete/CriteriaUpdate, and EntityGraph that controls what exactly to fetch. Criteria API is better since Java is OO. That is why JPA is created. When JPQL is compiled, it will be translated to AST tree(OO model) before translated to SQL.
Another point is that, I see Criteria is more suited for building on top of it and not to be used ditectly in the end-code.
It is more suited to build liberaries using it more than using jpql or hql.
For example I've build spring-data-jpa-mongodb-expressions using Criteria API (the same way spring data QBE do).
I think spring data query generations are using jpaql rather criteria which I don't understand why.
HQL can cause security concerns like SQL injection.
Related
I am trying to paginate results with the JPA criteria API. After looking through this answer: Total row count for pagination using JPA Criteria API
I see that the way to do it is to make a separate query that counts the number of rows returned by that specific criteria query. (They do this by making an array of predicates and using the same list for both the count query and the actual paginated query).
However, my criteria query cannot be replicated by a simple predicate list because it involves many joins with other tables and predicates involving comparisons with results returned from sub-queries.
I was wondering if there is a convenient way to get the row count for a particular criteria query without duplicating the query. (Or if there is an easy way to obtain a deep copy of a criteria query by passing in a reference)
Thanks in advance!
The "convenient way" to do this is to resort to native SQL and use window functions. You can easily add a window function to your top level SELECT that simply counts all the rows as follows:
SELECT x, y, ..., COUNT(*) OVER()
FROM ...
By using SQL instead of JPQL / Criteria API, you'll have access to a variety of other very useful features, like the above.
I am using hibernate with views (annotation #Subselect and #Synchronize) and filters. The problem is hibernate encapsulate query (defined by 'value' property of #Subselect annotation) and performances become very slow.
For example, if I write my own SQL query (which does not takes more than 100ms to execute) this is like that:
SELECT id,
country_id,
firstName,
lastName
FROM client
WHERE client.country_id IN (2564,2558,1452,3652)
But if I using hibernate with filters, the generated query will be:
SELECT _this.id AS id1_0_0,
_this.country_id AS country_id2_0_0
_this.firstName AS firstName3_0_0,
_this.lastName AS lastName4_0_0
FROM (
SELECT id,
country_id,
firstName,
lastName
FROM client
) _this
WHERE _this.country_id2_0_0 IN (2564,2558,1452,3652)
This means that the SQL generated by hibernate is slower than my sql query written by hand because all rows of the 'client' table (in nested query) are loaded, and filter operation is executed in parent query after having loaded all rows, this is stupid and provides very slow performances (1.7sec vs less than 100ms). So, does anyone known a method to avoid that ?
I can see various problems and solutions here:
look if you can avoid the #Subselect annotation at all => if you can create a view in the database that returns the requested data, it will make the whole scenario and implications (hibernate mapping) much easier (why should Hibernate do or optimize something, that databases are really optimized and designed for)
Hibernate does not generate code that is obviously optimized for your database (version) => try to switch to a newer Hibernate version or influence it with db-specific hints/configuration (dialect, vendor etc.)
the database does no good job at optimizing your query (filter push down) and seems very poor at such a common optimization strategy
=> try to switch to a newer db version or switch the DBMS if possible (e.g. PostgreSQL, Oracle, ...) since more future problems are to be expected here
(for more specific details one really needs to have more background information on your tables, db version and annotated classes)
I am looking for possible optimizations for framework-generated queries.
As far as I understand, the process is the following:
you could declare your domain objects as POJOs and adding several annotations like #Entity, #Table, #ManyToOneetc.
you declare your repositories e.g. per interfaces
With (2) you have several options to describe your query: e.g. per Methodnames or #Query
If I write a query like:
#Query("select t from Order t LEFT join fetch t.orderPositions where t.id = ?1")
Page<Order> findById(Pageable pageable, String id);
a SQL-query is autogenerated, where every column of the order is resolved and subsequentially for orderpositions and depending obejcts/tables.
As if I wrote:
select * from order
So in case, that I need some Information from several joined objects, a query could be quite expensive: and more interesting quite ineffective. I stumbled upon a slow query and MySQL-explain told me, that in the generated query the optimizer could not make use of indices, which is bad.
Of course (I am aware) I have to deal with a tradeoff, that generated SQL isn't as optimal as manually written and have the advantage of writing less boilerplate code.
My question is: what are good strategies to improve queries, queryexecution?
I have thought for some options by myself:
1) Is it possible to define several "Entities" for different purposes, like Order for access to the full characteristics of an order and something like FilteredOrder with fewer columns and no resolution of Join-columns? Both would reference the same tables, but one would use all of the columns and the other only some.
2) Use #Query(... native="true") with a selection of all columns, which I want to use. The advantage of that would be, that I would not double my domain-objects and litter my codebase with hundreds of Filtered-Objects.
What about paging? Is using pageable in combination with #Query( ...native="true") still possible (I am afraid not).
3) Last but in my eyes "worst"/boilerplate solution: Use JDBCTemplates and do stuff at a lower level.
Are there other options, of which I haven't thought?
Thank you for any inspiration on that topic :]
Update:
Our current strategy is the following
1) Where possible, I work with select new
As I have seen, this works for every Object (be it an Entity or POJO)
2) In combination with database views it is possible to take the best of SQL and ORM. For some usecases it might be of interest to have an aggregated resultset at hand. Defining this resultset as a view makes it easy from the db-perspective to watch the result with a simple select-statement.
For the ORM-side this means, you could easily define an entity matching this view and you get the whole ORM-goodness on top: Paging incl.
One solution is to use DTO's:
#Query("select new FilteredOrder(o.name, o.size, o.cost) from Order o where o.id = ?1")
Page<FilteredOrder> findFilteredOrderById(Pageable pageable, String id);
If you want to have entities for some reports generation maybe you should think about using nosql datastore?
Take a look at JPA's lazy fetching strategy. It will allow you to select objects without their relations, but will fetch the relations when you reference them.
Here are two queries retrieving equivalent data:
SELECT DISTINCT packStatus
FROM PackStatus packStatus JOIN FETCH packStatus.vars, ProcessEntity requestingProcess
WHERE
packStatus.status.code='OGVrquestExec'
AND packStatus.procId=requestingProcess.activityRequestersProcessId
AND requestingProcess.id='1000323733_GU_OGVProc'
SELECT DISTINCT packStatus
FROM PackStatus packStatus JOIN FETCH packStatus.vars
WHERE
packStatus.status.code='OGVrquestExec'
AND packStatus.procId=(SELECT requestingProcess.activityRequestersProcessId FROM ProcessEntity requestingProcess WHERE requestingProcess.id='1000323733_GU_OGVProc')
These queries differ in the method how requstingProcess is joined with packStatus. In general, which of these two methods is more preferable in terms of performance? I'm using JPA 1.2 provided by Hibernate 3.3 on Postgres 8.4.
UPD: I've replaced fake queries with real queries from my app. Here is SQL generated by Hibernate for first and second query. Links to query plans: first, second. Query plans look pretty the same. The only difference is what moment data from bpms_process table is aggregated to query result at. But I don't know is it right to generalize these results? Would query plans be almost the same for queries differing only in joining method? Is it possible to get a big difference in query cost by changing joining method?
Use EXPLAIN ANALYZE and see.
I won't be surprised if they get turned into the same query plan.
See: https://stackoverflow.com/tags/postgresql-performance/info
Our project must be able to run both in Oracle and SQL Server. The problem is we have a number of HQL + native queries with non-standard operators (i.e. bitand and || ) and functions ( i.e. SUBSTR ) that work fine in Oracle but not in SQL Server.
I wonder if Hibernate is capable of translating them dynamically. I suppose that with HQL maybe it does, because it creates an AST but I doubt the same applies with native queries.
Additional question: what's the best approach of dealing with these troublesome queries ? Conditionals, subclassing, others ... the goal is not to modify the code a lot.
Thanx in advance
Use custom Dialects for HQL. Instead of using ||, create your own function called concat. Then, in the SQL Server dialect add this to the constructor:
registerFunction("concat", new VarArgsSQLFunction(Hibernate.STRING, "", "+", ""));
You don't have to change the Oracle dialect because Oracle already has a concat function so it just passes through, but for other functions, you may need to register new functions in both.
For SQL queries, since you're building them dynamically anyway, you could use base class methods, for example super.addBitAndClause(leftSide, rightSide).
You can even get to the dialect dynamically, although Hibernate didn't make it easy by putting on the interface:
Dialect d = ((SessionFactoryImpl)sessionFactory).getDialect()
I suggest moving the HQL queries from the code to an external .hbm file and to use named queries before switching the Database. The HQL queries shouldn't be a problem as you already said. Native queries are a problem and you have to find the equivalent for the other DBMS. But by putting the queries into the external file you can then configure the sessionfactory to use the database specific .hbm file and do not need to change the code which depends only on the named query which can be a native query or a HQL
To get a named Query you can do the foloowing:
Query query = session.getNamedQuery("YourNamedHQLorSQLQuery");