NHibernate: Table aliases in SqlProjection - nhibernate

I have the following code:
Session.CreateCriteria<Foo>("foo")
.CreateAlias("foo.Bar", "bar")
.SetProjections(Projections.SqlProjection("bar.FirstName + ' ' + bar.LastName));
The problem is with the alias for the bar table in the SqlProjection.
The Hibernate docs say that "the string {alias} will be replaced by the alias of the root entity", but doesn't give any hint how you could access the aliases for non-root entities.
Is this possible?

I has a similar question. Specifically, I wanted to create a projection which was a concatenation of several fields. Instead of using SqlProjection, I used:
Projections.SqlFunction("concat",
NHibernateUtil.String,
Projections.Property("Field"),
Projections.Constant(" "),
Projections.Property("Field2",
Projections.Constant(" "),
Projections.Property("Field3")
);
This is based on an NHibernate unit test: UsingSqlFunctions_Concat_WithCast. This works on SQL Server, despite "concat" not being a native function.

Try this:
Session.CreateCriteria<Foo>()
.CreateCriteria("Bar")
.SetProjections(Projections.SqlProjection("FirstName + ' ' + LastName)");

Related

How best to process dynamic query parameters using Java & Spring

The problem:
I have an api endpoint which handles multiple query parameters. It is implemented using Spring, and the query parameters are used to query data from a postgres database, which I query with a JDBC Template.
I am searching for a mature query builder technology to solve my problem.
Example:
A trivial query could look something like this:
api/book?name=LOTR&cover=hardback
The query parameters are added to a map, and a query string is build from the maps data:
String sqlQuery += (String) map.entrySet().stream()
.map(entry -> entry.getKey() + "='" + entry.getValue() + "' AND ")
.collect(Collectors.joining());
Its not the most efficient, as I must always remove the trailing "AND" clause from the string, but it works.
However, if the query where to look something like
api/book?name=LOTR&name=Ulysses&cover=hardback
there is now the addition of an "OR" clause, which the above code would not handle. I can see myself quickly getting into the territory of with tedious string parsing to create SQL statements.
So now that I have presented my problem, I wonder if there is a technology I can use which handles this kind of problem nicely?
I would like to avoid the use of any ORM for this project, so Hibernate and MyBatis are out of the question. I have looked at some JOOQ examples, but they do not look compatible with JDBC Template.
For trivial implementations like your first case where you have to remove last AND after your query is built there is a simple hack - immediately after WHERE you add 1 = 1 and then for every WHERE predicate you add AND [COLUMN] = [VALUE].
Note: most databases optimise use of constants in WHERE clause before execution, so performance will not be an issue
/*
select <columns> from <tables> where 1 = 1
[dynamically built Where predicates will come here from following code]
*/
String sqlQuery += (String) map.entrySet().stream()
.map(entry -> "AND " + entry.getKey() + "='" + entry.getValue() + "'")
.collect(Collectors.joining());
However for serious production implementations you may want to use frameworks like myBatis that gives you possibilities of templating a query and then passing parameters at runtime to build final queries.
You can find a good tutorial here.
/* An example */
<select id = "getName_Id_phone" parameterType = "Student" resultType = "Student">
SELECT * FROM STUDENT
<where>
<if test = "id != null">
id = #{id}
</if>
<if test = "name != null">
AND name LIKE #{name}
</if>
</where>
</select>
Came across here with the same question. Maybe you want to take a look at RSQL 1

Drupal 7 - db_select: SQL function in where condition

I need to use this condition in my select statement:
WHERE YEAR(date) = YEAR(CURDATE())
but if I do, like this:
$query = db_select('table', 't');
$query->fields('t');
$query->condition('YEAR\(date\)', 'YEAR(CURDATE())', '=');
Drupal won't have it (even if I do not escape those parenthesis - it simply ignores them) because I get an error:
Column not found: 1054 Unknown column 'YEARdate' in 'where clause':
How to overcome this error?
Hmm.. just like this, it seems:
$query->where('YEAR(date) = YEAR(CURDATE())');
The where allows for the arbitrary SQL:
The where() method allows for the addition of arbitrary SQL as a conditional fragment. $snippet may contain any legal SQL fragment, and if it has variable content it must be added using a named placeholder. The $args array is an array of placeholders and values that will be substituted into the snippet. It is up to the developer to ensure that the snippet is valid SQL. No database-specific modifications are made to the snippet.
Hm, you could also use db_query, it allow you to write SQL queries "without Drupal".
I mean, you'll be able to add custom WHERE statements or any SQL-proper functions, like custom functions ;)
Eg.
$result = db_query('SELECT title FROM {node} WHERE type = "%s" AND title LIKE "%%%s%%"', 'type', 'title');
Use addExpression method :
https://api.drupal.org/api/drupal/includes!database!select.inc/function/SelectQuery%3A%3AaddExpression/7.x
$query = db_select('table', 't');
$query->fields('t');
$query->addExpression('YEAR(t.date) = YEAR(CURDATE())');
$result = $query->execute()->fetchAll();
var_dump($result);

NHibernate SQL query issue with string parameter

I got a problem with this code:
string sql = "select distinct ruoli.c_tip_usr"
+ " from vneczx_ute_app_trn ruoli"
+ " join vnecyd_ent_prf ind"
+ " on ruoli.c_ent = ind.c_ent"
+ " where ruoli.c_app = :appCode"
+ " and ruoli.c_ute_mat = :matricola"
+ " and ind.t_des_ent = :indirizzo";
var ruoli = session.CreateSQLQuery(sql)
.SetString("appCode", Config.Configurator.Istance.ApplicationCode)
.SetString("matricola", user.Matricola)
.SetString("indirizzo", indirizzoCasella)
.List<string>();
This code is correctly executed, the query logged is correct, and the parameter passed correctly evaluated... but it doesn't return any result at all.
Copying the query from the debug console and executing it directly in an Oracle client application (SQL Developer), it gets 2 results (the results I expect to be right).
I found out that the problem is in the last parameter indirizzo, and should depend on the fact that it contains a special char # (indirizzo is an email address).
So I ended up using this solution:
string sql = "select distinct ruoli.c_tip_usr"
+ " from vneczx_ute_app_trn ruoli"
+ " join vnecyd_ent_prf ind"
+ " on ruoli.c_ent = ind.c_ent"
+ " where ruoli.c_app = :appCode"
+ " and ruoli.c_ute_mat = :matricola"
+ " and ind.t_des_ent = '" + indirizzoCasella + "'";
var ruoli = session.CreateSQLQuery(sql)
.SetString("appCode", Config.Configurator.Istance.ApplicationCode)
.SetString("matricola", user.Matricola)
.List<string>();
But it gives me thrills! Aren't the parameters in a query supposed to handle specifically this situation, and thus handle themselves situations with special char, and so on?
Why here a string concatenation works better that a parametric query?
Isn't there a way to force the NHibernate engine to escape some special char?
Update:
I found out how to solve this particular issue: usign the trim command on the field who raise the problem, the problem disappears.
So last line of my sql string now is:
+ " and trim(ind.t_des_ent) = :indirizzo";
I can't understand why it solves the problem thought. Not the field, nor the variable contains empty chars and copying th query on SQL Developer works in both way.
So we have some luck soving the problem, but we have no idea why now it works?
Any thoughts?
even I was facing the same issue, but your hint using TRIM for column saved my day. Since I was looking for a long time, but not able to figure it out.
Along with that, I was able solve my issue by doing the below changes as well:
We were using CHAR datatype some of the columns which used in the query where clause. This was causing the issue to fetch the data from NHibernate. We changed the data type of that column from CHAR to VARCHAR2 and even updated the data with actual size and removed the TRIM from Where clause, the TRICK WORKED!!!! :)
So, any one face this kind of issue, first check whether you are having any column with CHAR and then change to VARCHAR2.
But one more thing you have to remember is: if you are running your application from ASP.Net Development Server, close it and re-run your application. Since if the opening Asp.Net Development Server and if you make any changes to datatype that will NOT be refreshed in your oracle connection. So, better you close the ASP.Net Development server and then re run the application.
Hope my points will help somebody in future!!
Regards,
Sree Harshavardhana
You are not using parameters in a SQL query if you want SQL parameters use a SQL stored proc

Using Hibernate DetachedCriteria for calling aggregate functions

I have a DetachedCriteria which I am using to search a table based on a name field. I want to make the search case-insensitive, and am wondering if there is a way to do this without using HQL. Something like:
private void searchByFullName(DetachedCriteria criteria, String searchCriteria) {
criteria.add(Restrictions.like("fullName", "%" + searchCriteria.toLowerCase() + "%"));
criteria.addOrder(Order.asc("fullName"));
}
But I want to make sure that it will ignore the case when it does the search (it has to search for both the upper and lower case), so the SQL it generates should look something like:
SELECT * FROM Student WHERE ? LIKE toLower(FULL_NAME);
What database are you using? MySQL LIKE is case-insensitive for CHAR, VARCHAR, and TEXT columns (I believe the same is true for SQL Server).
http://dev.mysql.com/doc/refman/5.5/en/case-sensitivity.html
If you're using PostgreSQL, you'll want to use the ILIKE operator, so you'll want to use Restrictions.ilike("fullName", name).
I see two options,
Option 1:
private void searchByFullName(DetachedCriteria criteria, String searchCriteria) {
criteria.add(Restrictions.like("toLower(fullName)", "%" + searchCriteria.toLowerCase() + "%"));
criteria.addOrder(Order.asc("fullName"));
}
Option 2:
private void searchByFullName(DetachedCriteria criteria, String searchCriteria) {
criteria.add(Restrictions.sqlRestriction("toLower({alias}.fullName) LIKE '%" + searchCriteria.toLowerCase() + "%'"));
criteria.addOrder(Order.asc("fullName"));
}
I am not very optimistic about Option 1. Option 2 should work for sure. {alias} is placeholder to let Hibernate know that it needs to add the appropriate alias for the table when it creates the SQL. More info http://docs.jboss.org/hibernate/core/3.5/api/org/hibernate/criterion/Restrictions.html#sqlRestriction%28java.lang.String%29

Alias of joined table in SQLProjection

I have this query:
criteria = session.CreateCriteria(typeof (Building))
.CreateAlias("Estate", "estate")
.SetProjection(Projections.ProjectionList()
.Add(Property.ForName("Name"), "BuildingName")
.Add(Property.ForName("estate.Name"), "EstateName")
.Add(Projections.SqlProjection(
"(estate1_.BBRMunicipalityNumber + '-' + estate1_.BBREstateNumber + '-' + {alias}.BBRBuildingNumber)" + " as BBRNumber",
new[] { "BBRNumber" },
new[] { NHibernateUtil.String }),
"BBRNumber"))
Is there a way that I can get the SQL alias for "estate" like writing {estate} in the SQL string? {estate} does not work. Now I ended up hardcoding the alias in the SQL string, but that doesn't seem very solid.
If I understand the docs correctly this should be possible. I'm using NH2.0.1.
/Asger
Not a direct answer to your question, but:
Why don't you query the three values separately and do the concatenation in your code instead of using the database for that?
To answer your question: In Hibernate v3 (java, sorry) there is a getColumnAlias method on the Projection interface. I'm not able to find its counterpart in NHibernate.
Cheers
You can use {alias} - it will reference the alias of the current projection.