I'd like to "dry-run" Hibernate HQL queries. That is I'd like to know what actual SQL queries Hibernate will execute from given HQL query without actually executing the HQL query against real database.
I have access to hibernate mapping for tables, the HQL query string, the dialect for my database. I have also access to database if that is needed.
Now, how can I find out all the SQL queries Hibernate can generate from my HQL without actually executing the query against any database? Are there any tools for this?
Note, that many SQL queries can be generated from one HQL query and the set of generated SQL queries may differ based on the contents of database.
I am not asking how to log SQL queries while HQL query is executing.
Edit: I don't mind connecting to database to fetch some metadata, I just don't want to execute queries.
Edit: I also know what limits and offsets are applied to query. I also have the actual parameters that will be bind to query.
The short answer is "you can't". The long answer is below.
There are two approaches you can take:
A) Look into HQLQueryPlan class, particularly its getSqlStrings() method. It will not get you the exact SQL because further preprocessing is involved before query is actually executed (parameters are bound, limit / offset are applied, etc...) but it may be close enough to what you want.
The thing to keep in mind here is that you'll need an actual SessionFactory instance in order to construct HQLQueryPlan, which means you won't be able to do so without "connecting to any database". You can, however, use in-memory database (SqlLite and the likes) and have Hibernate auto-create necessary schema for it.
B) Start with ASTQueryTranslatorFactory and descend into AST / ANTLR madness. In theory you may be able to hack together a parser that would work without relying on metadata but I have a hardest time imagining what is it you're trying to do for this to be worth it. Perhaps you can clarify? There has to be a better approach.
Update: for an offline, dry-run of some HQL, using HQLQueryPlan directly is a good approach. If you want to intercept every query in the app, while it's running, and record the SQL, you'll have to use proxies and reflection as described below.
Take a look at this answer for Criteria Queries.
For HQL, it's the same concept - you have to cast to Hibernate implementation classes and/or access private members, so it's not a supported method, but it will work with a the 3.2-3.3 versions of Hibernate. Here is the code to access the query from HQL (query is the object returned by session.createQuery(hql_string):
Field f = AbstractQueryImpl.class.getDeclaredField("session");
f.setAccessible(true);
SessionImpl sessionImpl = (SessionImpl) f.get(query);
Method m = AbstractSessionImpl.class.getDeclaredMethod("getHQLQueryPlan", new Class[] { String.class, boolean.class });
m.setAccessible(true);
HQLQueryPlan plan = (HQLQueryPlan) m.invoke(sessionImpl, new Object[] { query.getQueryString(), Boolean.FALSE });
for (int i = 0; i < plan.getSqlStrings().length; ++i) {
sql += plan.getSqlStrings()[i];
}
I would wrap all of that in a try/catch so you can go on with the query if the logging doesn't work.
It's possible to proxy your session and then proxy your queries so that you can log the sql and the parameters of every query (hql, sql, criteria) before it runs, without the code that builds the query having to do anything (as long as the initial session is retrieved from code you control).
Related
I am designing a testing framework that makes extensive use of SQL Sever Database. I am using Entity Framework 6 of .NET to felicitate it. I want to log the Underlying SQL query each time when I run a test case. I am using LINQ to SQL for querying Database.
I am having a hard time logging the SQL. LINQ to SQL generates some uncooked SQL which needs to be converted into SQL by filling in the parameters which I want to avoid.
Is there a better approach which will log all the SQL which I can directly feed to my SQL Server without doing any changes in Query ?
According to Entity Framework Logging:
The DbContext.Database.Log property can be set to a delegate for any method that takes a string. Most commonly it is used with any TextWriter by setting it to the “Write” method of that TextWriter. All SQL generated by the current context will be logged to that writer. For example, the following code will log SQL to the console:
using (var context = new BlogContext())
{
context.Database.Log = Console.Write;
// Your code here...
}
in the above way you should be able to log everything.
The following gets logged:
When the Log property is set all of the following will be logged:
SQL for all different kinds of commands. For example:
Queries, including normal LINQ queries, eSQL queries, and raw queries from methods such as SqlQuery
Inserts, updates, and deletes generated as part of SaveChanges
Relationship loading queries such as those generated by lazy loading
Parameters
Whether or not the command is being executed asynchronously
A timestamp indicating when the command started executing
Whether or not the command completed successfully, failed by throwing an exception, or, for async, was canceled
Some indication of the result value
The approximate amount of time it took to execute the command. Note that this is the time from sending the command to getting the
result object back. It does not include time to read the results.
Looking at the example output above, each of the four commands logged
are:
The query resulting from the call to context.Blogs.First
Notice that the ToString method of getting the SQL would not have worked for this query since “First” does not provide an
IQueryable on which ToString could be called
The query resulting from the lazy-loading of blog.Posts
Notice the parameter details for the key value for which lazy loading is happening
Only properties of the parameter that are set to non-default values are logged. For example, the Size property is only shown if it
is non-zero.
Two commands resulting from SaveChangesAsync; one for the update to change a post title, the other for an insert to add a new post
Notice the parameter details for the FK and Title properties
Notice that these commands are being executed asynchronously
I know that we can easily apply filters to query with additional where conditions with NHibernate, but is it possible to apply a filter when doing an update or delete?
If it is, how can I achieve that?
Yes it is possible, using HQL (Hibernate Query Language)
Here's an example of a batch update
IQuery updateQuery = this.Session.CreateQuery("update TransferItem set Status = :newStatus where Status = :oldStatus")
.SetParameter("oldStatus", DownloadStatus.Active)
.SetParameter("newStatus", DownloadStatus.Queued);
updateQuery.ExecuteUpdate();
NHibernate applies the configured mappings to create and run the following SQL:
update cms_TransferItem set Status=#p0 where Status=#p1
Here's an example of a batch delete
IQuery deleteQuery = this.Session.CreateQuery("delete TransferItem ti WHERE ti.Status = :statusToGo")
.SetParameter("statusToGo", DownloadStatus.Completed);
deleteQuery.ExecuteUpdate();
Which executes SQL like this:
delete from cms_TransferItem where Status=#p0
You might ask, if you have to work with a query language, why not just write raw SQL? When you use HQL you are working with the conceptual business objects that the rest of the .NET code is working with. The benefits of an ORM tool is that, for much of the code, the database tables and object-to-table mappings are abstracted away. With HQL you are continuing to interact with the object layer, rather than directly with the database tables.
I have a SQL statement String in Java which contains, among other things, the segment:
" AND \"Reference_No\" > ? "
I understand that this is a parameterized query, where the statement is precompiled and the parameters then added, in order to prevent injection attacks.
However, every example I've seen of this used, I have always seen accompanying code where the parameter values are then hard-coded in using some kind of setter method with code that runs something like:
setValue(1, "value1");
The program I am trying to understand does not appear to have this accompanying code, and I am trying to understand at what point a value is added to this SQL statement.
The application which uses this is a webUI servlet that sends and receives job transactions. More specifically, I am looking at the page that lists pending transactions.
I have a method which contains the following:
List<Job> query = getJdbcTemplate().query(sql.toString(),
new Object[]{minRef},
rowMapper);
sql contains the SQL statement segment in question.
Is the value-adding dealt with by the JdbcTemplate class? If so, how does it determine the values?
.Where(x => !x.Rated)
This creates sql that looks like:
not (cdrcalltmp0_.Rated=1)
Our dba says I have to remove the not for some filtered index to work.
.Where(x => x.Rated == false)
This creates sql that looks like:
cdrcalltmp0_.Rated=#p2 order by cdrcalltmp0_.Created asc'
This doesn't work because of the parameter.
He would like this sql:
cdrcalltmp0_.Rated=0 order by cdrcalltmp0_.Created asc'
Is it possible to make nhibernate not use parameters?
So that a filtered index works.
Preface:
The following answer is assuming you are using SQL Server 2008. If you are not, then it is quite possible that the database technology in question does not support indexes when using the NOT operator. So, if your using SQL Server 2008...
Your DBA doesn't know what he's talking about.
The following syntax
NOT ( SomeTableAlias.SomeTableColumn = 1 )
will absolutely be understood by the SQL Server Query Analyzer. I've got queries from NHibernate that look exactly like the above syntax and they are indeed using the proper indexes.
And to answer your question, no. NHibernate always uses parameters when it creates the SQL for you. Parameterized queries are extremely common place, even when using traditional ADO.NET yourself.
The only way to get NHibernate to not use parameters is if you supply the SQL it needs to execute yourself using the session.CreateSQLQuery() method.
At any rate, the above line you posted:
Where(x => x.Rated == false) This creates sql that looks like: cdrcalltmp0_.Rated=#p2 order by cdrcalltmp0_.Created asc'
is completely valid. When SQL Server receives the parameterized query, it will use whatever index is on your Rated column.
If your DBA still doubts you, tell him to run the query in Sql Server Management Studio with the "Display Estimated Execution Plan" feature on. That will prove that the query is using the index.
I am a SQL Server DBA for a company that sells an ASP.NET MVC3 application that uses LINQ and Entity Framework 4 for all database access. When I find an inefficient query in my SQL Server's plan cache that was generated by LINQ, I would like to be able to find that LINQ statement in the source code so that I can optimize it. What is the best way to find the LINQ that generated a given SQL statement?
For example, is there any way to put an entry in a config file or decorate the code somehow so that the class and method name or the LINQ statement itself are included as comments in the generated SQL?
The commercial tools ORM Profiler, Entity Framework Profiler or Hugati Query Profiler will both give you a stack trace for the methods which generated the SQL. That makes it fairly easy to find the LINQ in code, though it isn't displayed directly.
These tools also have the advantage that they make it easy to find inefficient queries amongst the many other SQL statements executed by the app.
Although it is not a free tool, this may provide the information you need:
http://efprof.com/
There is also a less expensive tool described here, which I have not used, but it looks very promising:
http://huagati.blogspot.com/2010/06/entity-framework-support-in-huagati.html
http://www.huagati.com/L2SProfiler/
I bet Entity Framework Profiler (http://efprof.com/) would help you out. The workflow is very different from what you asked for (which would be pretty cool BTW). It is a good tool, and is worth a look even if it's not your final solution.
Good luck!
If you have access to the ASP.NET code where the LINQ code is you can more or less know which query you are looking for, copy it into a freeware tool called LINQPad and run it directly there to get the generated SQL statements. http://www.linqpad.net/
You need first get the LINQ queries on your .net code, create a connection to your datasource, paste the Linq code in new queries and run them. You will get the SQL Query generated from the LINQ code.
For example:
from e in ETUSERs
where e.LoginName.Contains("a")
orderby e.LoginName
select e
SQL Results Tab:
-- Region Parameters
DECLARE #p0 VarChar(1000) = '%a%'
-- EndRegion
SELECT [t0].[UserID], [t0].[UsrFirstName], [t0].[UsrLastName], [t0].[LoginName], [t0].[Location], [t0].[Password], [t0].[UsrEmail], ...
FROM [ETUSER] AS [t0]
WHERE [t0].[LoginName] LIKE #p0
ORDER BY [t0].[LoginName]
This is probably not exactly what you are looking for, but it is worth knowing about this tool since it is very helpful to quickly test LINQ queries. There you can quickly edit and run to improve the code without recompiling the whole stuff.
I don't think you can modify the generated SQL easily but what you can do is to get the generated SQL before sending the query to the database.
So you can log every query in a separate textfile with timestamp and source code context information. But that means to modify each place in your source where LINQ queries are sent to the database. Maybe there is an extension point somewhere in the DataContext class for simplifying this.
Anyway here is the code to get the corresponding sql query for a LINQ query:
YourDataContext dc = new YourDataContext();
IQueryable<YourEntityClass> query =
from e in dc.YourEntities
where ...
select e;
string command = dc.GetCommand(query).CommandText;