I've been reading the documentation, but I couldn't find any information about it.
Is it possible to have Hibernate send user-provided SQL queries in order to UPDATE or to INSERT an object in the database?
In other words, is it possible to have session.saveOrUpdate( myObject ); , which generates update mySchema.myObject set field1=?, field2=?, field3=? where unique_key=?
to be replaced with a manual query from a user-provided String?
This is well described in the reference documentation. There are caveats, though : the session and the second-level cache aren't aware of the changes made to the entities, the version field is not updated, etc.
And if HQL is still not sufficient, you may always fall back to SQL queries.
Related
I am using Spring Boot with MyBatis. I have the following query in one mapper XML file.
<select id="someFunction" resultMap="someResultMap">
SELECT *
FROM p LEFT JOIN anotherDatabase.table AS q ON p.id = q.id
</select>
Actually "anotherDatabase" is hard-coded in my query because I do NOT want to add another data source for only this query. But how can I make this "anotherDatabase" name dynamically (maybe configure it in some properties file) as it may change in different environment deployed?
Though ugly solution, you can use a parameter: not a traditional JDBC/SQL parameter #{schema} but a direct parameter ${schema}. See the dollar sign ($) there?
When using a direct parameter ${param} you can insert whatever you want into the SQL. Even a entire whole SQL statement if you wish. Use it with care and only as a last resort.
Please carefully consider this insertion of direct parameters into the SQL is susceptible of SQL injection. You need to carefully control the value of the schema property/parameter, so it does not come from the user or any external source. If you do it like this, it will be safe to use.
However, a cleaner solution is to use a separate datasource. The only drawback is you may need to enable two-phase commit if you need transactions that emcompass tables from both datasources.
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
We are in the process of developing an "API" for one of our products. This will allow the user to define which columns they wan't to return from their "queries" and we will build the needed SQL.
I know that you should always use parameterized queries to avoid SQL injection attacks. However is there any security risks when building a statement where the columns returned are defined by the users? Lets say we have the following api request. This is just an example to illustrate what I mean :)
/api/customers/getall?fields=Name,Phone,Email&where=Zip=1000
The SQL will be
SELECT Name, Phone, Email FROM Customers WHERE Zip = #Zip
I'm not thinking about just taking the fields parameter and building the SQL directly around that, it will probably be made into a list and returned with some default columns like Id and Modified.
What should you be aware of in this situation? And how would you protect against attacks?
-- Christian
Create a Stored Procedure
This will let you check the input Stings and you should be on the safe side.
See:
MSDN Create Stored Procedures
First, I do a query to get the fields of the selected table(s) from the INFORMATION_SCHEMA.COLUMNS metadata table.
Querying database metadata
The column names pulled from the database are safe to use. Then I compared the fields in the SELECT clause with the "safe fields list". If one of the selected fields isn't on the clean list, then remove it or don't run the generated SQL at all.
I'm looking for a way to programmatically execute arbitrary SQL commands against my DB.
(Hibernate, JPA, HSQL)
Query.createNativeQuery() doesn't work for things like CREATE TABLE.
Doing LOTS of searching, I thought I could use the Hibernate Session.doWork().
By using the deprecated Configuration.buildSesionFactory() seems to show that doWork won't work.
I get "use lacks privilege or object not found" for all the CREATE TABLE statements.
So, what other technique is there for executing arbitratry SQL statements?
There were some notes on using the underlying JDBC Statement, but I haven't figure out how to get a JDBC Connection object from Hibernate to try that.
Note that the hibernate.hbm2ddl.auto=create setting will NOT work for me, as I have ARRAY[] columns which it chokes on.
I don't think there is any problem executing a create table statement with a Hibernate native query. Just make sure to use Query.executeUpdate(), and not Query.list() or Query.uniqueResult().
If it doesn't work, please tell us what happens when you execute it, and join the full stack trace of the exception and the SQL query you're executing.
"use lacks privilege or object not found" in HSQL may mean anything, for example existence of a table with the same name. Error messages in HSQL are completely misleading. Try listing your tables using DatabaseMetadata - you have probably already created the table.
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).