I would like to improve my code when deleting a group of objects in NHibernate (V3).
Currently, I iterate on a retrieved collection and I call delete on each object. This generates n+1 SQL statements.
I notice that NHibernate Session provides this method : Delete(string query)
By using this method I think I can do the same thing with a single SQL statement.
Do you know if there is a way to combine this method with QueryOver API to avoid HSQL ?
As far as I know the only way to do single-shot deletes and updates is using HQL. As a compromise, you might want to take a look at this workaround.
Related
I have a very silly doubt in NHibernate. There are two or three entities of which two are related and one is not related to other two entities. I have to fetch some selected columns from these three tables by joining them. Is it a good idea to use session.CreateSql() or we have to use session.CreateCriteria(). I am really confused here as I could not write the Criteria queries here and forced to use CreateSql. Please advise.
in general you should avoid writing SQL whenever possible;
one of the advantages of using an ORM is that it's implementation-agnostic.
that means that you don't know (and don't care) what the underlying database is, and you can actually switch DB providers or tweak with the DB structure very easily.
If you write your own SQL statements you run the risk of them not working on other providers, and also you have to maintain them yourself (for example- if you change the name of the underlying column for the Id property from 'Id' to 'Employee_Id', you'd have to change your SQL query, whereas with Criteria no change would be necessary).
Having said that- there's nothing stopping you from writing a Criteria / HQL that pulls data from more than one table. for example (with HQL):
select emp.Id, dep.Name, po.Id
from Employee emp, Department dep, Posts po
where emp.Name like 'snake' //etc...
There are multiple ways to make queries with NH.
HQL, the classic way, a powerful object oriented query language. Disadvantage: appears in strings in the code (actually: there is no editor support).
Criteria, a classic way to create dynamic queries without string manipulations. Disadvantages: not as powerful as HQL and not as typesafe as its successors.
QueryOver, a successor of Criteria, which has a nicer syntax and is more type safe.
LINQ, now based on HQL, is more integrated then HQL and typesafe and generally a matter of taste.
SQL as a fallback for cases where you need something you can't get the object oriented way.
I would recommend HQL or LINQ for regular queries, QueryOver (resp. Criteria) for dynamic queries and SQL only if there isn't any other way.
To answer your specific problem, which I don't know: If all information you need for the query is available in the object oriented model, you should be able to solve it by the use of HQL.
I am starting to play with (Fluent) nHibernate and I am wondering if someone can help with the following. I'm sure it's a total noob question.
I want to do:
delete from TABX where name = 'abc'
where table TABX is defined as:
ID int
name varchar(32)
...
I build the code based on internet samples:
using (ITransaction transaction = session.BeginTransaction())
{
IQuery query = session.CreateQuery("FROM TABX WHERE name = :uid")
.SetString("uid", "abc");
session.Delete(query.List<Person>()[0]);
transaction.Commit();
}
but alas, it's generating two queries (one select and one delete). I want to do this in a single statement, as in my original SQL. What is the correct way of doing this?
Also, I noticed that in most samples on the internet, people tend to always wrap all queries in transactions. Why is that? If I'm only running a single statement, that seems an overkill. Do people tend to just mindlessly cut and paste, or is there a reason beyond that? For example, in my query above, if I do manage it to get it from two queries down to one, i should be able to remove the begin/commit transaction, no?
if it matters, I'm using PostgreSQL for experimenting.
You can do a delete in one step with the following code:
session.CreateQuery("DELETE TABX WHERE name = :uid")
.SetString("uid", "abc")
.ExecuteUpdate();
However, by doing it that way you avoid event listener calls (it's just mapped to a simple SQL call), cache updates, etc.
Your first query comes from query.List<Person>().
Your actual delete statement comes from session.Delete(...)
Usually, when you are dealing with only one object, you will use Load() or Get().
Session.Load(type, id) will create the object for you without looking it up in the database . However, as soon as you access one of the object's properties, it will hydrate the object.
Session.Get(type, id) will actually look up the data for you.
As far as transactions, this is a good article explaining why it is good to wrap all of your nHibernate queries with transactions.
http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions
In NHibernate, I've noticed it is most common to do a delete with two queries like you see. I believe this is expected behavior. The only way around it off the top of my head is to use caching, then the first query could be loaded from the cache if it happened to be run earlier.
As far as wrapping everything in a transaction: in most databases, transactions are implicit for every query anyways. The explicit transactions are just a guarantee that the data won't be changed out from under you mid-operation.
I am building a website on top of nhibernate. Is there any efficient way to build reports? BY reports, I mean is there anyway to execute a complicated query that grabs random pieces of data? Stored procedures? Hql?
Can I get single, non mapped values from hql?
Yes you can.
For simple stuff you can use HQL and select stuff into a new non-mapped object (its class must be "registered" with NH but there is no mapped per se). HQL syntax looks like
select new NonMappedClass(column1, column2). You need an appropriate constructor to use this.
Reporting with HQL quickly breaks down. I often find myself knowning exactly what to do in SQL but having hard time figuring out the HQL way. Also the lack of real software tools for HQL slows you down (sorry NH Query Analyzer does not cut it). In these case, you can define a raw with an associated . When executing these queries, NH will return you a IList of array of Object. You need the cast the Object() into the proper type. You'll want this for advanced reporting queries.
This casting stuff is error prone and sucks big time. So you they did a nice AliasToBean query transformer that can map the result set column name to a property (names must match of course). I don't know about the latest NH release, but the older 1.2.1 AliasToBean converter seemed to have a bug in which it would converter nullable value type to the default value of the type instead of setting it to null. ie: int? would be 0 instead of null if the associated DB field was null. This prevented me from using AliasToBean in some case and I had to map by hand.
Best advice is don't fall into the trap of performing complex reporting with your business object and for loops. I've seen this in production. It will be a performance horror as your tables grows in size.
Yes, you can do this. It's called dynamic instantiation. The syntax in HQL is
select new MyClass(cust.age)
from customers cust
Or, with Criteria:
.SetProjection(Projections.ProjectionList()
.Add(Projections.Name("cust.age"), "age")
.SetResultTransformer(Transformers.AliasToBean<MyClass>())
Remember, you must have a matching constructor on MyClass!
I'm using a stored procedure to handle search on my site, it includes full text searching, relevance and paging. I also wanted it to return the total number of results that would have been returned, had paging not being there. So I've now got my SP returning 2 select statements, the search and just SELECT #totalResults.
Is there any way I can get NHibernate to handle this? I'm currently accessing the ISession's connection, creating a command and executing the SP myself, and mapping the results. This isn't ideal, so I'm hoping I can get NH to handle this for me.
Or if anyone has any other better ways of creating complicated searches etc with NH, I'd really like to hear it.
No, NHibernate only uses the first result set returned by the stored procedure and ignores any others.
You will need to use an alternative method, like ADO.NET.
Or, you can incur processing overhead by having two stored procedures. One for each result set. Gross.
Does any one have some idea on how to run the following statement with LINQ?
UPDATE FileEntity SET DateDeleted = GETDATE() WHERE ID IN (1,2,3)
I've come to both love and hate LINQ, but so far there's been little that hasn't worked out well. The obvious solution which I want to avoid is to enumerate all file entities and set them manually.
foreach (var file in db.FileEntities.Where(x => ids.Contains(x.ID)))
{
file.DateDeleted = DateTime.Now;
}
db.SubmitChanges();
There problem with the above code, except for the sizable overhead is that each entity has a Data field which can be rather large, so for a large update a lot of data runs cross the database connection for no particular reason. (The LINQ solution is to delay load the Data property, but this wouldn't be necessary if there was some way to just update the field with LINQ to SQL).
I'm thinking some query expression provider thing that would result in the above T-SQL...
LINQ cannot perform in store updates - it is language integrated query, not update. Most (maybe even all) OR mappers will generate a select statement to fetch the data, modify it in memory, and perform the update using a separate update statement. Smart OR mappers will only fetch the primary key until additional data is required, but then they will usually fetch the whole rest because it would be much to expensive to fetch only a single attribute at once.
If you really care about this optimization, use a stored procedure or hand-written SQL statement. If you want compacter code, you can use the following.
db.FileEntities.
Where(x => ids.Contains(x.ID)).
Select(x => x.DateDeleted = DateTime.Now; return x; );
db.SubmitChanges();
I don't like this because I find it less readable, but some prefer such an solution.
LINQ to SQL is an ORM, like any other, and as such, it was not designed to handle bulk updates/inserts/deleted. The general idea with L2S, EF, NHibernate, LLBLGen and the rest is to handle the mapping of relational data to your object graphs for you, eliminating the need to manage a large library of stored procs which, ultimately, limit your flexability and adaptability.
When it comes to bulk updates, those are best left to the thing that does them best...the database server. L2S and EF both provide the ability to map stored procedures to your model, which allows your stored procs to be somewhat entity oriented. Since your using L2S, just write a proc that takes the set of identities as input, and executes the SQL statement at the beginning of your question. Drag that stored proc onto your L2S model, and call it.
Its the best solution for the problem at hand, which is a bulk update. Like with reporting, object graphs and object-relational mapping are not the best solution for bulk processes.