NHibernate - easiest way to do a LIKE search against an integer column with Criteria API? - nhibernate

I'm trying to do a like search against an integer column, what I need to do is actually cast the column to a varchar and then do the like search. Is this possible? what's the easiest way to do this using the Criteria API?
var search = "123";
criteria.Add(Restrictions.Like("Number", "%" + search + "%"))

If Number were a string, then it would be easy :
.Add(Restrictions.Like("Number", "some_value",MatchMode.Anywhere))
Since you have a number, NHibernate will check the type of Number and if you give it a string it will throw an exception.
Not sure why the NH team didn't provide an overload with object as parameter and a MatchMode parameters ....
Anyhow, you can still do it like this :
.Add(Expression.Sql("{alias}.Number like ?", "%2%", NHibernateUtil.String))
Edit
About the alias :
(i can't find where the documentation talks about this but here's my understanding of it )
{alias} returns the alias used inside by NH for the most recent CreateCriteria. So if you had :
session.CreateCriteria<User>("firstAlias")
.CreateCriteria("firstAlias.Document", "doc")
.Add(Expression.Sql("{alias}.Number like ?", "%2%",
NHibernateUtil.String)).List<User>();
{alias} in this case would be 'doc' - so you would end up with : doc.Number .
So, always use {alias} after the CreateCriteria whose alias you need to use.

Related

How can I perform a regex/text search on a SUPER type?

What I'm doing now:
I have a table with one field that is a json value that is stored as a super type in my staging schema.
the field containing the json is called elements
In my clean table, I typecast this field to VARCHAR in order to search it and use string functions
I want to search for the string net within that json in order to determine the key/value that I want to use for my filter
I tried the following:
select
elements
, elements_raw
from clean.events
where 1=1
and lower(elements) like '%net%'
or strpos(elements,'net')
My output
When running the above query, I keep getting an empty set returned.
My issue
I tried running the above code and using the elements_raw value instead but I got an issue :ERROR: function strpos(super, "unknown") does not exist Hint: No function matches the given name and argument types. You may need to add explicit type casts.
I checked the redshift super page and it doesn't list any specifics on searching strings within super types
Desired result:
Perform string operations on super field
Cast super field to a string type
There are some super related idiosyncrasies that are being run into here:
You cannot change the type of a super field via :: or cast()
String functions like and strpos do not work on super types
To address both of these issues, you can use the function json_serialize to return your super as a string.

VB.NET + LINQ: Save in a single class attribute the result of querying two columns from different tables

I have two tables: Estructura (with two fields I want: descripcion_morologica and interpretacion) and estrato (with two fields I want: descripcion_larga and interpretacion_explic). On the other side I hace a class in VS with the attributes descripcion and interpretacion. Both tables have a common field called id_excavacion, which I pass to the method as a parameter.
What I'm trying to achieve is to make a query which saves in the "descripcion" attribute the results of t1.descripcion_morfologica and t2.descripcion_larga and also saves in "interpretacion" the results of t1.interpretacion and t2.interpretacion_explic.
So far, I've tried like this:
'GET: api/Excavacions/ListadoUE/5
<Route("api/Excavacions/ListadoUE/{idExcavacion}")>
Function GetListadoUEs(ByVal idExcavacion As Integer) As IQueryable(Of ListadoUEDto)
Dim listado =
From estru In db.Estructura
Join estra In db.Estrato On estra.id_excavacion Equals estru.id_excavacion
Where estru.id_excavacion = idExcavacion
Select New ListadoUEDto With {
.Descripcion = estru.descripcion_morfologica And estra.descripcion_larga,
.Interpretacion = estru.interpretacion And estra.interpretacion_explic
}
End Function
But I only get null, despite the id I pass actually exists.
Thanks a lot in advance!!
You should almost certainly not be using And there. That is a Boolean operator, for combining True and False values. As is always the case, if you want to concatenate Strings then you use &, which is the string concatenation operator.
You should have Option Strict On and then the compiler would have warned you that you were doing something that doesn't make sense. You should turn it On in the project properties and also in the VS options, so it is On by default for future projects. That will force you to put more thought into what data types you use and, therefore, make you write better code.

QueryDSL + PathBuilder + cast to string

I am filtering PrimeFaces DataTables using dynamic filters.I have this working using Spring org.springframework.data.jpa.domain.Specification.Now I am wondring how to do the same using QueryDSL.
Using specification I can use javax.persistence.criteria.Root to get a javax.persistence.criteria.Join, use javax.persistence.criteria.Expression.as(Class<String> type) to cast it to String and finally use javax.persistence.criteria.CriteriaBuilder.like(Expression<String> x, String pattern, char escapeChar).
How do I do the same in QueryDSL ? I can get PathBuilder using new PathBuilder<T>(clazz, "entity") (do you really have to use the variable here? I would like my class to be generic...) but then the com.mysema.query.types.path.PathBuilder.get(String property) returns new PathBuilder instead of an Expression.
If I try to use com.mysema.query.types.path.PathBuilder.getString(String property) I get java.lang.IllegalArgumentException: Parameter value [1] did not match expected type [java.lang.Integer].
Seems like the part I am missing is the cast.
I'm quite sure someone was dealing with the same thing already.
Thanks.
Edit: Stack trace for IllegalArgumentException
Trying to search for text "1" inside integer column using com.mysema.query.types.path.PathBuilder.getString(String property) - that's where I need the cast to happen :
Caused by: java.lang.IllegalArgumentException: Parameter value [1] did not match expected type [java.lang.Integer]
at org.hibernate.ejb.AbstractQueryImpl.validateParameterBinding(AbstractQueryImpl.java:375)
at org.hibernate.ejb.AbstractQueryImpl.registerParameterBinding(AbstractQueryImpl.java:348)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:375)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:442)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:72)
at com.mysema.query.jpa.impl.JPAUtil.setConstants(JPAUtil.java:44)
at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:130)
at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:97)
at com.mysema.query.jpa.impl.AbstractJPAQuery.list(AbstractJPAQuery.java:240)
at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.findAll(QueryDslJpaRepository.java:102)
...
To get a valid condition you will need to take the types of the properties into account, e.g.
pathBuilder.getNumber(Integer.class, property).stringValue().like(likePattern)
I was researching a similar topic until I came across this aged question. I hope my answer will be helpful to some.
I think the possible solution lies (exclusively) outside of QueryDSL. You can use common Java reflection to obtain the field type, something like
Class type = clazz.getDeclaredField(criteria.getKey()).getType();
// don't forget to catch exception if field doesn't exist ..
switch(type.getSimpleName()) {
case "String":
StringExpression exp = entityPath.getString(...);
}
That way you can have a reasonably dynamic implementation.

Do I have to prefix sql parameter name with # sign when adding SqlParameters to the collection? [duplicate]

In one of our application the parameters passed to a stored procedure in this way
Dim parm As New SqlParameter("searchText", SqlDbType.VarChar)
parm.Direction = ParameterDirection.Input
parm.Size = 50
parm.Value="test"
cmd.Parameters.Add(parm)
and the procedure contains a parameter as #searchText
i.e. the parameter name passed from the code is searchText and that in the stored procedure is #searchText .
But it is working properly, I am always getting the required results.
So my question is like so there is no need to specify # before the parameter? Whether it will append #, can anyone please give an answer for this.
According to the documentation, the name must start with an #:
The ParameterName is specified in the form #paramname.
According to the source code (have a look at SqlCommand and SqlParameter.ParameterNameFixed in the reference source), an # is added automatically, if needed.
So yes, it works, but it's an undocumented feature. Best practice recommends that you do not rely on this and manually prefix your parameter name with an #.
Ref: SqlParameter.ParameterName Property and IDataParameter.ParameterName Property
The ParameterName is specified in the form #paramname. You must set ParameterName before executing a SqlCommand that relies on parameters. If you are using Sql Server as Database then you must specify # before
the parameter name.
your parameter name must be same as at backend eg. you have #searchText then in your parameter specification it must be SqlParameter("#searchText" ..
your code should be like this
Dim parm As New SqlParameter("#searchText", SqlDbType.VarChar)
parm.Direction = ParameterDirection.Input
parm.Size = 50
parm.Value="test"
cmd.Parameters.Add(parm)
Note: Oracle and SqLite use different use different character to specify parameter and there may be # symbol is not used specified by the specification of ado.net.
Edit: By comments
As you specified the link, it is also some sort of fix, but as per the msdn documentation, you must specify the positional parameter with '#' whether you are using any data provider oledb, sql, odbc. Ref
if (0 < parameterName.get_Length() && '#' != parameterName.get_Chars(0))
{
parameterName = "#" + parameterName;
}
Its not compulsory to specify the #. However, its a best practice.
Its similar in analogy to strings. There certainly is no harm in defining strings as such in .NET:
string s;
//Rest of the code follows;
But again, its a best practice to define them as :
string s = string.Empty;
You see, its a question of conventions and best practices!!!
I recommended you to use add "#" marker with your parameter name.
SqlParameter helps to add automatically, but others' parameter might not to.
Is the "#" symbol required? Yes and No. When you add a parameter using DbCommand, it's optional regardless of whether you're using SQL Server or not:
// Look Ma no # required!
DbCommand command = database.GetStoredProcCommand("StoredProctologistAndGambler");
database.AddInParameter(command, "Bet", DbType.Int32, fromLineNumber);
database.AddOutParameter(command, "Diagnosis", DbType.String, -1);
If you're going to reference the command later, however, the "#" prefix is required. Microsoft figured it was to hard to carry it over to the rest of the API.
var examResult = command.Parameters["#Diagnosis"]; // Ma! Microsoft lied! It requires the "#" prefix.

How to run a function using a nhibernate criteria?

We have a search routine that uses criteria to build SQL query (because its restrictions added dynamically).
In a particular case (a very complicated case) we need to search over a table-valued function.(our model object is mapped to the function).
The result would be something like this :
SELECT count(*) FROM dbo.GetSubStaffsLetterInstances(#staffId) WHERE LetterNumber="1234";
The problem is I don't know how to pass #staffId to my criteria(I tried adding an Eq restrictions without success since restrictions are working on properties)
I know I can add a parameter to an IQuery but I don't know how I can do it using an ICriteria object.
If I understand your question completely, you can resort back to standard SQL:-
var sql = "SELECT count(*) FROM dbo.GetSubStaffsLetterInstances(:staffId)
WHERE LetterNumber=:letterNum";
var count = session.CreateSqlQuery(sql)
.setInt32("staffId", 12345)
.setString("letternum", "A1")
.UniqueResult<int>();
or try .UniqueResult<long>(); as I can't remember which one HQL returns