Nhibernate Linq & operator RegisterFunction Firebird - nhibernate

I am using NHibernate with Firebird and would like to create the bitwise and operator to the Firebird function bin_and(a, b)
Something like this:
var result = customers.Where(c => (c.StatusValue & 3) > 0);
The above query will result in something like that:
select * from customers where (StatusValue & 3) > 0
Which is not valid in Firebird, the result should be:
select * from customers where bin_and(StatusValue,3) > 0
Is there a possibility to overwrite this translated result ?
Update
By declaring a function this is possible:
[LinqExtensionMethod("BIN_AND")]
public static int BinAnd(int a, int b)
{
return a & b;
}
var result = customers.Where(c => BinAnd(c.StatusValue, 3) > 0);
This works, but I am searching for a more generic way wizh the '&' or '|' operator...
Update:
# Fédéric:
I wrote my own Dialect class like this:
public class MyFirebirdDialect: FirebirdDialect {
public MyFirebirdDialect()
{
// Bitwise operations
RegisterFunction("band", new BitwiseFunctionOperation("bin_and"));
RegisterFunction("bor", new BitwiseFunctionOperation("bin_or"));
RegisterFunction("bxor", new BitwiseFunctionOperation("bin_xor"));
RegisterFunction("bnot", new BitwiseFunctionOperation("bin_not"));
}
}
I had to import the BitwiseFunctionOperation.cs too
If I debug the code I see that this class is used as Dialect, and I see that there is a custom function for the key 'band' that has a value 'bin_and' but a Query like this
var result = customers.Where(c => (c.StatusValue & 3) > 0);
ends up in an sql like this :
select * from customers where (StatusValue & 3) > 0
I think the linq parser does not its part...

Are you using the appropriate dialect? FirebirdDialect correctly defines bitwise and in HQL (RegisterFunction("band", new BitwiseFunctionOperation("bin_and")); and linq-to-nhibernate translate & (ExpressionType.And) to the appropriate HQL call.
If you are using an old NHibernate version, maybe you need to upgrade.
Firebird bitwise operators have been added with NH-3630 in NHibernate 4.1.
You may try to back-port them in your project by using a custom dialect deriving from FirebirdDialect and registering those additional functions as illustrated in the link above, within your custom dialect constructor.
But that will not work, because it requires some other changes in NHibernate internals, not available before NHibernate 4.1. Maybe by patching a local copy of NHibernate 3.4 sources may you succeed in doing that.

Related

Slick plain sql query with pagination

I have something like this, using Akka, Alpakka + Slick
Slick
.source(
sql"""select #${onlyTheseColumns.mkString(",")} from #${dbSource.table}"""
.as[Map[String, String]]
.withStatementParameters(rsType = ResultSetType.ForwardOnly, rsConcurrency = ResultSetConcurrency.ReadOnly, fetchSize = batchSize)
.transactionally
).map( doSomething )...
I want to update this plain sql query with skipping the first N-th element.
But that is very DB specific.
Is is possible to get the pagination bit generated by Slick? [like for type-safe queries one just do a drop, filter, take?]
ps: I don't have the Schema, so I cannot go the type-safe way, just want all tables as Map, filter, drop etc on them.
ps2: at akka level, the flow.drop works, but it's not optimal/slow, coz it still consumes the rows.
Cheers
Since you are using the plain SQL, you have to provide a workable SQL in code snippet. Plain SQL may not type-safe, but agile.
BTW, the most optimal way is to skip N-th element by Database, such as limit in mysql.
depending on your database engine, you could use something like
val page = 1
val pageSize = 10
val query = sql"""
select #${onlyTheseColumns.mkString(",")}
from #${dbSource.table}
limit #${pageSize + 1}
offset #${pageSize * (page - 1)}
"""
the pageSize+1 part tells you whether the next page exists
I want to update this plain sql query with skipping the first N-th element. But that is very DB specific.
As you're concerned about changing the SQL for different databases, I suggest you abstract away that part of the SQL and decide what to do based on the Slick profile being used.
If you are working with multiple database product, you've probably already abstracted away from any specific profile, perhaps using JdbcProfile. In that case you could place your "skip N elements" helper in a class and use the active slickProfile to decide on the SQL to use. (As an alternative you could of course check via some other means, such as an environment value you set).
In practice that could be something like this:
case class Paginate(profile: slick.jdbc.JdbcProfile) {
// Return the correct LIMIT/OFFSET SQL for the current Slick profile
def page(size: Int, firstRow: Int): String =
if (profile.isInstanceOf[slick.jdbc.H2Profile]) {
s"LIMIT $size OFFSET $firstRow"
} else if (profile.isInstanceOf[slick.jdbc.MySQLProfile]) {
s"LIMIT $firstRow, $size"
} else {
// And so on... or a default
// Danger: I've no idea if the above SQL is correct - it's just placeholder
???
}
}
Which you could use as:
// Import your profile
import slick.jdbc.H2Profile.api._
val paginate = Paginate(slickProfile)
val action: DBIO[Seq[Int]] =
sql""" SELECT cols FROM table #${paginate.page(100, 10)}""".as[Int]
In this way, you get to isolate (and control) RDBMS-specific SQL in one place.
To make the helper more usable, and as slickProfile is implicit, you could instead write:
def page(size: Int, firstRow: Int)(implicit profile: slick.jdbc.JdbcProfile) =
// Logic for deciding on SQL goes here
I feel obliged to comment that using a splice (#$) in plain SQL opens you to SQL injection attacks if any of the values are provided by a user.

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

How to use a dynamic parameter in a IN clause of a JPA named query?

my problem is about this kind of query :
select * from SOMETABLE where SOMEFIELD in ('STRING1','STRING2');
the previous code works fine within Sql Developer.
The same static query also works fine and returns me a few results;
Query nativeQuery = em.createNativeQuery(thePreviousQuery,new someResultSet());
return nativeQuery.getResultList();
But when I try to parameterize this, I encounter a problem.
final String parameterizedQuery = "select * from SOMETABLE where SOMEFIELD in (?selectedValues)";
Query nativeQuery = em.createNativeQuery(parameterizedQuery ,new someResultSet());
nativeQuery.setParameter("selectedValues","'STRING1','STRING2'");
return nativeQuery.getResultList();
I got no result (but no error in console).
And when I look at the log, I see such a thing :
select * from SOMETABLE where SOMEFIELD in (?)
bind => [STRING1,STRING2]
I also tried to use no quotes (with similar result), or non ordered parameter (:selectedValues), which leads to such an error :
SQL Error: Missing IN or OUT parameter at index:: 1
I enventually tried to had the parentheses set directly in the parameter, instead of the query, but this didn't work either...
I could build my query at runtime, to match the first (working) case, but I'd rather do it the proper way; thus, if anyone has an idea, I'll read them with great interest!
FYI :
JPA version 1.0
Oracle 11G
JPA support the use of a collection as a list literal parameter only in JPQL queries, not in native queries. Some JPA providers support it as a proprietary feature, but it's not part of the JPA specification (see https://stackoverflow.com/a/3145275/1285097).
Named parameters in native queries also aren't part of the JPA specification. Their behavior depends on the persistence provider and/or the JDBC driver.
Hibernate with the JDBC driver for Oracle support both of these features.
List<String> selectedValues = Arrays.asList("STRING1", "STRING2");
final String parameterizedQuery = "select * from SOMETABLE where SOMEFIELD in (:selectedValues)";
return em.createNativeQuery(parameterizedQuery)
.setParameter("selectedValues", selectedValues)
.getResultList();
Instead of:
nativeQuery.setParameter("selectedValues", params);
I had to use:
nativeQuery.setParameterList("selectedValues", params);
This worked for me in derby. parameter without "()".
List<String> selectedValues = Arrays.asList("STRING1", "STRING2");
final String parameterizedQuery = "select * from SOMETABLE where SOMEFIELD in
:selectedValues";
return em.createNativeQuery(parameterizedQuery)
.setParameter("selectedValues", selectedValues)
.getResultList();
Replace this:
nativeQuery.setParameter("selectedValues","'STRING1','STRING2'");
with
List<String> params;
nativeQuery.setParameter("selectedValues",params);
I also faced the same issue.
This is what I did:
List<String> sample = new ArrayList<String>();
sample.add("sample1");
sample.add("sample2");
And now you, can set the sample in params.

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

LINQ display row numbers

I simply want to include a row number against the returned results of my query.
I found the following post that describes what I am trying to achieve but gives me an exception
http://vaultofthoughts.net/LINQRowNumberColumn.aspx
"An expression tree may not contain an assignment operator"
In MS SQL I would just use the ROWNUMBER() function, I'm simply looking for the equivalent in LINQ.
Use AsEnumerable() to evaluate the final part of your query on the client, and in that final part add a counter column:
int rowNo = 0;
var results = (from data in db.Data
// Add any processing to be performed server side
select data)
.AsEnumerable()
.Select(d => new { Data = d, Count = ++rowNo });
I'm not sure whether LINQ to SQL supports it (but it propably will), but there's an overload to the Queryable.Select method that accepts an lambda with an indexer. You can write your query as follows:
db.Authors.Select((author, index) => new
{
Lp = index, Name = author.Name
});
UPDATE:
I ran a few tests, but unfortunately LINQ to SQL does not support this overload (both 3.5sp1 and 4.0). It throws a NotSupportedException with the message:
Unsupported overload used for query
operator 'Select'.
LINQ to SQL allows you to map a SQL function. While I've not tested this, I think this construct will work:
public partial class YourDataContext : DatContext
{
[Function(Name = "ROWNUMBER")]
public int RowNumber()
{
throw InvalidOperationException("Not called directly.");
}
}
And write a query as follows:
from author in db.Authors
select new { Lp = db.RowNumber(), Name = author.Name };