hibernate - createCriteria or createAlias? - nhibernate

If I want to search those students who take class "Math" and "John" is his group:
shoud I use createCriteria or createAlias?
Criteria:
Criteria criteria = session.createCriteria(Student.class);
Criteria subquery1 = criteria.createCriteria("courses", course).add(Restrictions.eq(course.name, "Math"));
Criteria subquery2 = criteria.createCriteria("group", student).add(Restrictions.eq(student.name, "John"));
how to put subquery1 and subquery2 together with initial criteria?
Alias:
Criteria criteria = session.createCriteria(Student.class).
createAlias("courses", course).add(Restrictions.eq(course.name, "Math")).
createCriteria("group", student).add(Restrictions.eq(student.name, "John"));
When to use createCriteria and when createAlias? I think the boath are the same...

CreateAlias and CreateCriteria are identical in the current versions of Hibernate and NHibernate. The only difference being that CreateCriteria has 2 additional overloads without the alias parameter.
Presumably they were different in a older version, but any differences are long gone.
An alias can be defined in terms of another alias, so your first example can be written as:
// Java
Criteria criteria = session.createCriteria(Student.class)
.createAlias("courses", "course")
.createAlias("course.group", "student")
.add(Restrictions.eq("course.name", "Math"))
.add(Restrictions.eq("student.name", "John"));
// C#
ICriteria criteria = session.CreateCriteria<Student>()
.CreateAlias("Courses", "course")
.CreateAlias("course.Group", "student")
.Add(Restrictions.Eq("course.Name", "Math"))
.Add(Restrictions.Eq("student.Name", "John"));

Adding to xavierzhoa's answer:
There is actually quite a big difference between the two methods which you will notice if you chain Criteria methods. You will continue to work on the original Criteria object when using createAlias, whereas you work on a more nested scope when using createCriteria.
Consider this:
Criteria c = getSession()
.createCriteria(YourEntity.class)
.createCriteria("someMember", "s")
.add(Restrictions.eq("name", someArgument)); // checks YourEntity.someMember.name
versus
Criteria c = getSession()
.createCriteria(YourEntity.class)
.createAlias("someMember", "s")
.add(Restrictions.eq("name", someArgument)); // checks YourEntity.name
However, if you always assign and use an alias you will be able to work around the difference. Like:
Criteria c = getSession()
.createCriteria(YourEntity.class, "y")
.createAlias("someMember", "s")
.add(Restrictions.eq("y.name", someArgument)); // no more confusion

Please refer to the following source code from the Hibernate
public Criteria createCriteria(String associationPath, String alias, int joinType) {
return new Subcriteria( this, associationPath, alias, joinType );
}
public Criteria createAlias(String associationPath, String alias, int joinType) {
new Subcriteria( this, associationPath, alias, joinType );
return this;
}

Criteria criteria = (Criteria)sessionFactory.getCurrentSession().createCriteria(BillEntity.class)
.createAlias("worksEntity", "worksEntity" ,JoinType.LEFT_OUTER_JOIN)
instead of writing (Jointype) use the import file as
(org.hibernate.sql.JoinType..LEFT_OUTER_JOIN)

Related

Query builder dbflow conditional query

how can i build a query based on certain conditions .
i tried doing this
QueryBuilder builder = SQlite.Select().from(Table)
if(condition) {
builder.where(something)
}
Cursor c = builder.query;
but it is not permitted.
I have to query my database on conditions that i have saved in preferences . I googled and searched everywhere in thr docs but couldn't find a single example . do this feature exists in dbflow if yes then how if no is thr any other orm (like greenDAO) with this feature
You can create conditional queries in DBFlow. To query a table's columns you must append _Table to your class name, then access its property. These _Table classes are generated during build time.
The most simple query would be this one:
SQLite.select()
.from(YourTable.class)
.where(YourTable_Table.id.eq(someId)
.queryList();
You can also add new conditions by using .and and .or in your query:
SQLite.select()
.from(YourTable.class)
.where(YourTable_Table.id.eq(someId)
.and(YourTable_Table.name.eq(someName)
.queryList();
For a cleaner code, you can also group conditions into condition groups like this:
ConditionGroup conditionGroup = ConditionGroup.clause();
conditionGroup.and(YourTable_Table.id.eq(someId);
if (someCondition) {
conditionGroup.and(YourTable_Table.name.eq(someName);
}
return SQLite.select()
.from(YourTable.class)
.where(conditionGroup)
.queryList();
found two ways of achieving my problem
1.from #trevjonez(trevor jones)
Where<SomeModel> query = SQLite.select()
.from(SomeModel.class)
.where(SomeModel_Table.date_field.greaterThan(someValue));
if(someCondition) {
query = query.and(SomeModel_Table.other_field.eq(someOtherValue));
} else {
query = query.and(SomeModel_Table.another_field.isNotNull());
}
Cursor cursor = query.query();
//do stuff with cursor and close it
—
2.from #zshock using ConditionalGroup
ConditionGroup conditionGroup = ConditionGroup.clause();
conditionGroup.and(YourTable_Table.id.eq(someId);
if (someCondition) {
conditionGroup.and(YourTable_Table.name.eq(someName);
}
return SQLite.select()
.from(YourTable.class)
.where(conditionGroup)
.queryList();

Entity Framework filter data by string sql

I am storing some filter data in my table. Let me make it more clear: I want to store some where clauses and their values in a database and use them when I want to retrieve data from a database.
For example, consider a people table (entity set) and some filters on it in another table:
"age" , "> 70"
"gender" , "= male"
Now when I retrieve data from the people table I want to get these filters to filter my data.
I know I can generate a SQL query as a string and execute that but is there any other better way in EF, LINQ?
One solution is to use Dynamic Linq Library , using this library you can have:
filterTable = //some code to retrive it
var whereClause = string.Join(" AND ", filterTable.Select(x=> x.Left + x.Right));
var result = context.People.Where(whereClause).ToList();
Assuming that filter table has columns Left and Right and you want to join filters by AND.
My suggestion is to include more details in the filter table, for example separate the operators from operands and add a column that determines the join is And or OR and a column that determines the other row which joins this one. You need a tree structure if you want to handle more complex queries like (A and B)Or(C and D).
Another solution is to build expression tree from filter table. Here is a simple example:
var arg = Expression.Parameter(typeof(People));
Expression whereClause;
for(var row in filterTable)
{
Expression rowClause;
var left = Expression.PropertyOrField(arg, row.PropertyName);
//here a type cast is needed for example
//var right = Expression.Constant(int.Parse(row.Right));
var right = Expression.Constant(row.Right, left.Member.MemberType);
switch(row.Operator)
{
case "=":
rowClause = Expression.Equal(left, right);
break;
case ">":
rowClause = Expression.GreaterThan(left, right);
break;
case ">=":
rowClause = Expression.GreaterThanOrEqual(left, right);
break;
}
if(whereClause == null)
{
whereClause = rowClause;
}
else
{
whereClause = Expression.AndAlso(whereClause, rowClause);
}
}
var lambda = Expression.Lambda<Func<People, bool>>(whereClause, arg);
context.People.Where(lambda);
this is very simplified example, you should do many validations type casting and ... in order to make this works for all kind of queries.
This is an interesting question. First off, make sure you're honest with yourself: you are creating a new query language, and this is not a trivial task (however trivial your expressions may seem).
If you're certain you're not underestimating the task, then you'll want to look at LINQ expression trees (reference documentation).
Unfortunately, it's quite a broad subject, I encourage you to learn the basics and ask more specific questions as they come up. Your goal is to interpret your filter expression records (fetched from your table) and create a LINQ expression tree for the predicate that they represent. You can then pass the tree to Where() calls as usual.
Without knowing what your UI looks like here is a simple example of what I was talking about in my comments regarding Serialize.Linq library
public void QuerySerializeDeserialize()
{
var exp = "(User.Age > 7 AND User.FirstName == \"Daniel\") OR User.Age < 10";
var user = Expression.Parameter(typeof (User), "User");
var parsExpression =
System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] {user}, null, exp);
//Convert the Expression to JSON
var query = e.ToJson();
//Deserialize JSON back to expression
var serializer = new ExpressionSerializer(new JsonSerializer());
var dExp = serializer.DeserializeText(query);
using (var context = new AppContext())
{
var set = context.Set<User>().Where((Expression<Func<User, bool>>) dExp);
}
}
You can probably get fancier using reflection and invoking your generic LINQ query based on the types coming in from the expression. This way you can avoid casting the expression like I did at the end of the example.

how to return the sum of a value in a table with where clause in grails 2.5.0

Domain class:
class Transaction {
String roundId
BigDecimal amount
:
}
The SQL we wish to execute the following:
"select sum(t.amount) from transaction t where t.roundId = xxx"
We have been unable to find an example which does not return Transaction rows.
We assume there are two approaches:
Use projections and/or criteria etc? All the examples we have found only return lists of transaction rows, not the sum.
Use raw SQL. How do we call SQL, and get a handle on the BigDecimal it returns?
I tried this:
class bla{
def sessionFactory
def someMethod() {
def SQLsession = sessionFactory.getCurrentSession()
def results = SQLsession.createSQLQuery("select sum(t.credit) from transaction t where t.round_id = :roundId", [roundId: roundId])
But this fails with
groovy.lang.MissingMethodException: No signature of method: org.hibernate.internal.SessionImpl.createSQLQuery() is applicable for argument types: (java.lang.String, java.util.LinkedHashMap)
Also, I have no idea what the return type would be (cant find any documentation). I am guessing it will be a list of something: Arrays? Maps?
==== UPDATE ====
Found one way which works (not very elegant or grails like)
def SQLsession = sessionFactory.getCurrentSession()
final query = "select sum(t.credit) from transaction t where t.round_id = :roundId"
final sqlQuery = SQLsession.createSQLQuery(query)
final results = sqlQuery.with {
setString('roundId', roundId)
list() // what is this for? Is there a better return value?
}
This seems to return an array, not a list as expected, so I can do this:
if (results?.size == 1) {
println results[0] // outputs a big decimal
}
Strangely, results.length fails, but results.size works.
Using Criteria, you can do
Transaction.withCriteria {
eq 'roundId', yourRoundIdValueHere
projections {
sum 'amount'
}
}
https://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/classic/Session.html
Query createSQLQuery(String sql, String[] returnAliases, Class[] returnClasses)
Query createSQLQuery(String sql, String returnAlias, Class returnClass)
The second argument of createSQLQuery is one or more returnAliases and not meant for binding the statement to a value.
Instead of passing your values in the 2nd argument, use the setters of your Query object i.e. setString, setInteger, etc.
results.setInteger('roundId',roundId);

Ordering a query by the string length of one of the fields

In RavenDB (build 2330) I'm trying to order my results by the string length of one of the indexed terms.
var result = session.Query<Entity, IndexDefinition>()
.Where(condition)
.OrderBy(x => x.Token.Length);
However the results look to be un-sorted. Is this possible in RavenDB (or via a Lucene query) and if so what is the syntax?
You need to add a field to IndexDefinition to order by, and define the SortOption to Int or something more appropriate (however you don't want to use String which is default).
If you want to use the Linq API like in your example you need to add a field named Token_Length to the index' Map function (see Matt's comment):
from doc in docs
select new
{
...
Token_Length = doc.TokenLength
}
And then you can query using the Linq API:
var result = session.Query<Entity, IndexDefinition>()
.Where(condition)
.OrderBy(x => x.Token.Length);
Or if you really want the field to be called TokenLength (or something other than Token_Length) you can use a LuceneQuery:
from doc in docs
select new
{
...
TokenLength = doc.Token.Length
}
And you'd query like this:
var result = session.Advanced.LuceneQuery<Entity, IndexDefinition>()
.Where(condition)
.OrderBy("TokenLength");

Need help optimizing a NHibernate criteria query that uses Restrictions.In(..)

I'm trying to figure out if there's a way I can do the following strictly using Criteria and DetachedCriteria via a subquery or some other way that is more optimal. NameGuidDto is nothing more than a lightweight object that has string and Guid properties.
public IList<NameGuidDto> GetByManager(Employee manager)
{
// First, grab all of the Customers where the employee is a backup manager.
// Access customers that are primarily managed via manager.ManagedCustomers.
// I need this list to pass to Restrictions.In(..) below, but can I do it better?
Guid[] customerIds = new Guid[manager.BackedCustomers.Count];
int count = 0;
foreach (Customer customer in manager.BackedCustomers)
{
customerIds[count++] = customer.Id;
}
ICriteria criteria = Session.CreateCriteria(typeof(Customer))
.Add(Restrictions.Disjunction()
.Add(Restrictions.Eq("Manager", manager))
.Add(Restrictions.In("Id", customerIds)))
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("Name"), "Name")
.Add(Projections.Property("Id"), "Guid"))
// Transform results to NameGuidDto
criteria.SetResultTransformer(Transformers.AliasToBean(typeof(NameGuidDto)));
return criteria.List<NameGuidDto>();
}
return Session.CreateCriteria<Customer>()
.CreateAlias("BackupManagers", "bm", LeftOuterJoin)
.Add(Restrictions.Disjunction()
.Add(Restrictions.Eq("Manager", manager))
.Add(Restrictions.Eq("bm.Id", manager.Id)))
.SetProjection(Projections.Distinct(Projections.ProjectionList()
.Add(Projections.Property("Name"), "Name")
.Add(Projections.Property("Id"), "Guid")))
.SetResultTransformer(Transformers.AliasToBean(typeof(NameGuidDto)))
.List<NameGuidDto>();
I threw a distinct in there because I'm not sure if it is possible to have backup and primary be the same person.