Can I get the SQL string from a JPA Query object? - sql

May I know how can I get the sql from a JPA query? or let's say, convert the JPA query to a SQL string? Thank you very much!

For Eclipselink: you can extract the SQL the following way:
query.unwrap(EJBQueryImpl.class).getDatabaseQuery().getSQLString()
It works only after the query has been executed.

If you only want to know how your JPQL or Criteria Query gets translated to the SQL dialect of your database you can enable fine grained logging in the persistence xml and then look into your log files.
The property name and value depends on your JPA implementation. Here is an example of the relevant part of persistence.xml for EclipseLink:
<properties>
<property name="eclipselink.logging.level" value="FINEST"/>
</properties>

JPA Specification
While there is not standard JPA functionality to achieve this goal, you can still extract the SQL query from a JPQL or Criteria API Query using the JPA provider-specific API.
Hibernate Types
Starting with the 2.9.11 version, the Hibernate Types open-source project offers the SQLExtractor utility that allows you to get the SQL query from any JPQL or Criteria API query, no matter you are using Hibernate 5.4, 5.3, 5.2, 5.1, 5.0, 4.3, 4.2, or 4.1.
Get the SQL statement from a JPQL Query
Let's assume we have the following JPQL query:
Query jpql = entityManager.createQuery("""
select
YEAR(p.createdOn) as year,
count(p) as postCount
from
Post p
group by
YEAR(p.createdOn)
""", Tuple.class
);
With Hibernate Types, extracting the Hibernate-generated SQL query is as simple as that:
String sql = SQLExtractor.from(jpql);
And, if we log the extracted SQL query:
LOGGER.info("""
The JPQL query: [
{}
]
generates the following SQL query: [
{}
]
""",
jpql.unwrap(org.hibernate.query.Query.class).getQueryString(),
sql
);
We get the following output:
- The JPQL query: [
select
YEAR(p.createdOn) as year,
count(p) as postCount
from
Post p
group by
YEAR(p.createdOn)
]
generates the following SQL query: [
SELECT
extract(YEAR FROM sqlextract0_.created_on) AS col_0_0_,
count(sqlextract0_.id) AS col_1_0_
FROM
post p
GROUP BY
extract(YEAR FROM p.created_on)
]
Notice that we unwrapped the JPA Query to the Hibernate org.hibernate.query.Query interface which provided the getQueryString method we can use to log the associated JPQL query string.
Get the SQL statement from a JPA Criteria API Query
The SQLExtractor is not limited to JPQL queries. You can use it with Criteria API queries as well, as illustrated by the following example:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<PostComment> criteria = builder.createQuery(PostComment.class);
Root<PostComment> postComment = criteria.from(PostComment.class);
Join<PostComment, Post> post = postComment.join("post");
criteria.where(
builder.like(post.get("title"), "%Java%")
);
criteria.orderBy(
builder.asc(postComment.get("id"))
);
Query criteriaQuery = entityManager.createQuery(criteria);
String sql = SQLExtractor.from(criteriaQuery);
assertNotNull(sql);
LOGGER.info("""
The Criteria API, compiled to this JPQL query: [
{}
]
generates the following SQL query: [
{}
]
""",
jpql.unwrap(org.hibernate.query.Query.class).getQueryString(),
sql
);
When running the above test case, we get the following SQL query:
- The Criteria API, compiled to this JPQL query: [
select
pc
from
PostComment as pc
inner join
pc.post as p
where
p.title like :param0
order by
pc.id asc
]
generates the following SQL query: [
SELECT
pc.id AS id1_1_,
pc.post_id AS post_id3_1_,
pc.review AS review2_1_
FROM
post_comment pc
INNER JOIN
post p ON pc.post_id=p.id
WHERE
p.title LIKE ?
ORDER BY
pc.id ASC
]
The Criteria API is first compiled to a JPQL query, as illustrated by the getQueryString() method call.
The intermediary JPQL query is further translated to an SQL query, which is properly resolved by the SQLExtractor utility.

Following Karol's answer - It is possible to retrieve the SQL before executing the statement in EclipseLink :
Session session = em.unwrap(JpaEntityManager.class).getActiveSession();
DatabaseQuery databaseQuery = query.unwrap(EJBQueryImpl.class).getDatabaseQuery();
databaseQuery.prepareCall(session, new DatabaseRecord());
Record r = databaseQuery.getTranslationRow();
String bound = databaseQuery.getTranslatedSQLString(session, r);
String sqlString = databaseQuery.getSQLString();
To retrieve the SQL String During/After execution it is probably best to do so using persistence properties rather than in-code :
<property name="eclipselink.logging.parameters" value="true"/>
<property name="eclipselink.logging.level" value="FINE"/>

Beside enabling the logging like #Matt Handy mentioned it is also possible to get the SQL String for a specific query with eclipselink at runtime as described here.

Using Hibernate as a provider you can enable the following properties:
hibernate.show_sql
Write all SQL statements to console. This is an alternative to setting the log category org.hibernate.SQL to debug. (e.g. true | false)
hibernate.format_sql
Pretty print the SQL in the log and console. (e.g. true | false)
Or, as stated above you can enable logging to the debug level for the logger
org.hibernate.SQL
Log all SQL DML statements as they are executed

You are probably interested if there's a way to 'extract' JPQL string (either with placeholders for params, or final JPQL after params are filled-in) out of javax.persistence.Query (one of it's possible subclasses to be more precise),- in this case it's not possible according to JPA specification contract. However, this hypothetically might be possible by JPA implementation (e.g., NamedQueryImpl could have #toJPQLString(), which you could access via casting), but I doubt about that.
And even if it's possible I don't think it's a good code performing such manipulations. I would suggest finding another design solutions (and for that you could specify what kind of actual problem do you have). E.g., if you are building your queries dynamically, JPA Criteria API could be used for that, and along with 'building' JPA query, you could maintain your internal data structure reflecting the logic of your query.

This blog contains instructions: http://narcanti.keyboardsamurais.de/hibernate-hql-to-sql-translation.html

You could use p6spy. At the following link there are instructions for its operation:
https://p6spy.readthedocs.io/en/latest/install.html

Related

Jpa contains command not standard

I use spring data jpa
contains command seem to do full-text search and don't care of accentued characted
#Query("""
select a from Author a
where (contains(a.name,:name)>0 ) and a.externalAuthor is not null
""")
List<Author> findByName(String name);
I have this query that work well, native is not specified.
contains don't seem a standard jpa command so I don't understand why that work
Edit, I search to use contains in a Criteria query

OpenJPA:JPQL vs native query

I am using OpenJPA in my work.
Sometimes I have to use JPQL and sometimes I have to use native query(em.createNativQuery).
I am witnessing a big issue with native query. I have to provide schema name too.
Like for JPQL I can write:
em.createQuery("Select e from Entity_name e").getResultList();
But in case of native query I need to do:
em.createNativeQuery("Select e from SCHEMANAME.Table_name e").getResultList()
Why is it so?And isn't this wrong behaviour as schema name may vary with time.
Try specifying the following property in your configuration.
<property name="openjpa.jdbc.Schema" value="SCHEMANAME"/>
If you use both JPQL and native queries, try to add the actual entity as follows.
e.g Query query = em.createNativeQuery("SELECT * FROM MAG", Magazine.class);
Otherwise you might face data inconsistency issues like dirty reads, lost updates .etc
UPDATE
Query createNativeQuery(java.lang.String sqlString,
java.lang.Class resultClass)
Create an instance of Query for executing a native SQL query.
Parameters: sqlString - a native SQL query string
resultClass - the class of the resulting instance(s)
Hope this helps.

What's the generated query in sql for setFirstResult function in hibernate

What’s the generated query in sql server for this query in hibernate:
Criteria c = session.createCriteria(...);
c.setFirstResult(10);
c.setMaxResults(20);
Try executing it and watching your console for the query that's being created.
Since you're using multiple criteria, you'll need to experiment to see the exact effect that setFirstResult is having.

Hibernate: Build a list of Unique Strings - not entities

I am storing and retrieving Java Entities in a DB using Hibernate.
I need to build up (for the purposes of creating web-page 'drop-downs' / 'lookups') a list of unique strings.
NB: I don't really want to retrieve back Entities here as such - I want to run the equivalent of a SQL 'SELECT DISCTINCT(column) FROM table;' and get back a list of strings.
Is there a standard Hibernate Idiom for doing this - or should I use another mechanism ?
Hibernate query is supporting that query, you can use either hql or native query to get String.
Query query = session.createQuery("select distinct user.firstname from User as user");
or
Query query = session.createNativeQuery("select distinct user.firstname from User user");
List<String> list = (List<String>) query.list();
Reference: Hibernate Query

NHibernate Lambda expressions - are they turned into SQL

I'm a bit of a NHibernate newbie and Im taking on some code written by another developer. I want to find out how NHibernate converts lambda based criteria into SQL.
I know in Linq to SQL using Lambda expressions on queries means that the whole thing is turned into an expression tree and then into SQL (where possible) by the Linq to SQL provider. This can be seen by doing DataContext.Log = Console.Out.
But what about an NHibernate criteria expression where Linq to NHibernate isnt being used?
The following namespaces are imported...
using NHibernate;
using NHibernate.Criterion;
using NHibernate.LambdaExtensions;
.. and the criteria code looks like this...
return Session.CreateCriteria<MyObjectType>()
.Add<MyObjectType>(x => x.Id == id)
.UniqueResult<MyObjectType>();
Will this be turned into an SQL statement e.g.
Select distinct * from table where id = [param]
... or will the whole dataset be pulled into memory giving a List and then have the lambda expressions applied against the objects. e.g.
return List<MyObject>.Where(x => x.id = id) [or something similar].
I', not sure if my importing NHibernate.LambdaExtensions provides a sort of translation into SQL.
It is turned to an HQL statement first (enable logging and look at the console for the statements) and then to an SQL and sent to the database.
It does not select the whole table to memory and filters there.