how to get query string from a javax.persistence.Query? - java

Maybe I am missing something, but I simply want to (in my java program) get the query string from a javax.persistence.Query object?
The Query object itself doesn't seem to have a method to do that. Also I know that our manager doesn't want us to use Spring framework stuff (for example using their QueryUtils class).
Is there not a way to simply get the query string from javax.persistence.Query object (Again, in a java program) ?!

no problem. hibernate:
query.unwrap(org.hibernate.Query.class).getQueryString()
or eclipselink
query.unwrap(JpaQuery.class).getDatabaseQuery().getJPQLString(); // doesn't work for CriteriaQuery
query.unwrap(JpaQuery.class).getDatabaseQuery().getSQLString();
or open JPA
query.unwrap(org.apache.openjpa.persistence.QueryImpl.class).getQueryString()
...you get the idea....

There is no JPA-standard way, but some implementations have their own methods. For example DataNucleus JPA allows you to do
query.toString();
Look at the docs of your implementation for how they do it. See also this blog entry
http://antoniogoncalves.org/2012/05/24/how-to-get-the-jpqlsql-string-from-a-criteriaquery-in-jpa/

There is no standard way in JPA,
If you are using EclipseLink see,
http://wiki.eclipse.org/EclipseLink/FAQ/JPA#How_to_get_the_SQL_for_a_Query.3F

Edit your persistence properties file if JPA is provided by hibernate. Add the below section -
<property name="hibernate.show_sql" value="true"/>

Using OpenJPA I was able to do this:
OpenJPAQuery<MyEntity> q = (OpenJPAQuery<MyEntity>) query;
String mySQL = q.getQueryString();
The cast to OpenJPAQuery is important because the Query interface does not have the method getQueryString() on it.
The stored parameters are not filled in unfortunately but I'm working on figuring out how to do that myself.

Related

Custom SQL for Order in JPA Criteria API

I'm switching from deprecated (unfortunately) Hibernate Criteria API to JPA Criteria API. We have a custom Order (from Hibernate) interface implementation to redefine SQL generated for it. The case is quite sophisticated as we need to use a giant SELECT with subqueries. We implemented toSqlString method of the interface to return this huge SQL and we need a way to migrate it to JPA Criteria API.
The question is: is there a way in JPA Criteria API to redefine the SQL generated? Or is there a weird way to use Hibernate Order with JPA Criteria API?
Thank you!
UPDATE Although #Tobias Liefke suggestion is quite interesting, my SQL varies too much to create a function class per SQL. I tried implementing a single function class and passing the SQL there as an argument but that didn't work (the rendered SQL was enclosed in single quotes thus it was sent to the database as parameter and not as part of the generated query)
You can't use SQL fragments in JPQL or criteria queries...
... except when ...
1. Calling a function
JPA and Hibernate allow to use functions in their expressions, for example:
... ORDER BY trim(entity.label) ASC
Resp.
query.orderBy(criteriaBuilder.asc(
criteriaBuilder.function("trim", String.class, root.get(ExampleEntity_.label))));
The problem is, that this is not really the call to the SQL function trim, but the call to a JPA function, which must be registered (Hibernate does this already for the most common SQL functions).
Fortunately you can define your own JPA functions in a DialectResolver:
public class MyDialectResolver implements DialectResolver {
public Dialect resolveDialect(final DialectResolutionInfo info) {
Dialect dialect = StandardDialectResolver.INSTANCE.resolve(info);
dialect.registerFunction("myOrderFunction", ...);
return dialect;
}
}
registerFunction takes two parameters, the first is the name of the function in JPA, the other is the mapping to SQL.
Don't forget to declare your dialect resolver in your persistence.xml:
<persistence-unit name="database">
<properties>
<property name="hibernate.dialect_resolvers"
value="my.package.MyDialectResolver" />
</properties>
</persistence-unit>
You could now create your own function in your SQL server which contains your huge SQL and register that as function:
dialect.registerFunction("myOrderFunction",
new StandardSQLFunction("myOrderFunctionInSQL", StringType.INSTANCE));
Or you could write your own mapping, which includes your huge SQL:
public class MyOrderFunction implements SQLFunction {
public String render((Type firstArgumentType, List arguments,
SessionFactoryImplementor factory) throws QueryException) {
return my_huge_SQL;
}
// ...
}
And register that one:
dialect.registerFunction("myOrderFunction", new MyOrderFunction());
Another advantage of this solution: you could define different SQLs depending on the actual database dialect.
2. Using a formula
You could use an additional attribute for your entity:
#Formula("my huge SQL")
private String orderAttribute;
You could now sort by this attribute:
... ORDER BY entity.orderAttribute ASC
Resp.
query.orderBy(criteriaBuilder.asc(root.get(ExampleEntity_.orderAttribute))));
I only recommend this solution, if you need the result of the huge SQL in your model anyway. Otherwise it will only pollute your entity model and add the SQL to every query of your entity (except you mark it with #Basic(fetch = FetchType.lazy) and use byte code instrumentation).
A similar solution would be to define a #Subselect entity with the huge SQL - with the same drawbacks.

Is there a HQL version of Hibernate's Restrictions.sqlRestriction()?

I have a Java application using Hibernate 4.3.6. We use two different databases (one for regular deploy, other for unit/integration tests). There's a common db-function we'd like to use, but it's called different in each db dialect and Hibernate has no support for it. We've fixed this by simply creating subclasses for each Dialect and using:
this.registerFunction("normalizedFunctionName",
new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "arbitraryFunctionName(?1, ?2)"));
I can now use normalizedFunctionName(?, ?) in HQL. However I'd like to use it when using the Criteria API, something like:
sessionFactory.getCurrentSession()
.createCriteria(SomeClass.class)
.add(
Restrictions.lt("normalizedFunctionName(value, 'bla')", 3)
);
But this doesn't work. Now I've discovered there's:
Restrictions.sqlRestriction("arbitraryFunctionName(value, 'bla') < 3");
But since that's native SQL and not HQL, I can't use it.
So, my questions are:
Is there an HQL version of the Restrictions.sqlRestriction() feature?
Or is there another alternative to accomplish what I'm trying to do?

How to tell Hibernate annotation #Column to be case-sensitive?

I'm trying to do a simple SELECT query in a table named ECM (in uppercase) on a Sybase db with Hibernate. I've annotated my DBO this way :
#Entity
#Table(name="ECM")
public class RelationshipDbo {
...
}
However, I'm facing a "table not found" error : the generated SQL has the table name in lowercase. I cannot change the database configuration to tell it to be case-insensitive.
I've also tried putting quotes like this :
#Table(name="`ECM`")
and this :
#Table(name="'ECM'")
Result : the quotes are added in the query, but the table name is still converted from uppercase to lowercase.
Technical information :
Hibernate 4.3
JPA 1.2
org.hibernate.dialect.SybaseDialect
Have you guys any idea?
EDIT: Also tried this Hibernate changes #Table(name) to lowercase
Then my columns names and table name are automatically quoted, but the names still get lowercased.
I think I have your answer:
Basically, you need to change the naming strategy for you JPA provider. How you do this will depend on how you setup your project.
In my case, using spring boot data I set a property in my application.properties to
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.EJB3NamingStrategy
Without more details from you I can't give more specifics on how to do this.
My goal is a little different since was trying to create tables upper case and hibernate created them in lower case. Also i was using MySQL not Sybase.
But for me quoting the names like this worked:
#Entity
#Table(name="\"ECM\"")
public class RelationshipDbo {
...
}
Then tables were created upper case. Maybe that helps also for the queries.
What is your Sybase db version ?
SybaseDialect has been deprecated in Hibernate 3.5 and then refactored since Hibernate 4.1 with a bunch of subclasses matching different versions of Sybase. Have you tried one of the subclasses to see if it makes any difference?
org.hibernate.dialect.Sybase11Dialect
org.hibernate.dialect.SybaseAnywhereDialect
org.hibernate.dialect.SybaseASE15Dialect
Try this:
Use backticks as in #Table(name="`ECM`")?
This must work from Hibernate point. If not then problem should be in DB (if i'm not wrong)

How do i group by date only from date time field in JPQL

For mysql I wrote a query like
SELECT * FROM mytable GROUP BY DATE(dateTimeField)
But i can't use the DATE() function in JPQL.
Anyone have an idea how to resolve this problem?
If You use Hibernate under the hood you can try :
Create your own dialect with custom function
public class CustomPostgresqlDialect extends PostgreSQLDialect {
public CustomPostgresqlDialect() {
super();
registerFunction("truncate_date",
new SQLFunctionTemplate( StandardBasicTypes.TIMESTAMP, "DATE(?1)" ));
}
}
Register Your dialect in persistence.xml
<property name="hibernate.dialect" value="my.own.CustomPostgresqlDialect"/>
Use your function in JPQL.
select p from Post p group by truncate_date(p.dateCreated)
For Hibernate:
Suggestion 1:
Use cast(dateTimeField as date). See here.
Suggestion 2:
You can try to concatenate year, month and year HQL expressions.
Take a look at line 360 of this testcase.
str(year(current_date))||'-'||str(month(current_date))||'-'||str(day(current_date))
But take the time to see the generated SQL, it may (and probably will be) ugly and slow.
If you are using EclipseLink you can use the FUNC() function in JPQL to call a specific database function:
Support for Native Database Functions Using FUNC
I'm afraid that's beyond the scope of what JPQL offers. You will have to resort to native queries.
Here's the List of available functions in JPQL:
Java EE Tutorial 6 > Persistence > JPQL > Functional Expressions

Created query is not supported by my DB

I created an application using seam-gen. The created operation to search the DB ends with and exception (syntax error). The query has a where clause like this:
lower(barcode0_.barcode_ean) like lower((?||'%')) limit ?
Does hibnerate or seam create the where clause which my DB can't understand?
Or is there a workaround for SQL statement related issues in seam?
I'm not an expert of Seam but this looks like the problem mentioned in this thread (see also JBSEAM-3297). The suggested solution is use lower(concat instead of concat(lower in seam-gen/src/EntityList.java.ftl.

Categories