I have read the documentation and explanation on why it is highly recommended to use transactions on read operations in NH. However, I still haven't totally "bought" into it yet. Can someone take a stab at explaining it without just telling me to RTFM, which I have already done? ;)
var fooIdFromDb = ExecuteQuery("Select Id from Foo where something = somethingelse");
var barsFromDb = ExecuteQuery("Select * from Bar where FooId = " + fooIdFromDB);
What if some other transaction deletes rows from Bar between the two queries? You will have issues with phantom data. This is not an NHibernate specific issue. You will have the same problem with any other kind of database access without using transactions. You should read the manual about transactions in general instead of the NHiberante manual.
This post from one of the authors might have your answer:
Even if we are only reading data, we want to use a transaction, because using a transaction ensure that we get a consistent result from the database. NHibernate assume that all access to the database is done under a transaction, and strongly discourage any use of the session without a transaction.
Leaving aside the safety issue of working with transactions, the assumption that transactions are costly and we need to optimize them is a false one. As already mentioned, databases are always running in transaction. And databases have been heavily optimized to work with transactions. The question is whatever this is per statement or per batch. There is some amount of work that need to be done to create and dispose a transaction, and having to do it per statement is actually more costly than doing it per batch.
What the others have said is true, but they've not pointed out that the problem with not controlling transactions yourself is that if you perform several NHibernate operations without an explicit transaction, each of these operations will take place in separate transactions.
So you can easily get inconsistency between the operations. By explicitly starting an NHibernate transaction then performing the operations in it, you're guaranteed consistency across these operations.
This is, of course, true for ANY data access layer that implicitly starts transactions for you if you do not. It's not limited to NHibernate.
Let's focus on what happens if you would not use transactions. It's customary, but not mandatory, that you close the Session at the end of processing, but before you start reading the data (i.e., the View). This method is propagated under the term "Open Session in View" (though it obviously has a pattern for preventing read before close). This pattern is often used in web application where the session is opened when the request arrives and closed just before writing to the response stream.
(N)Hibernate needs a session and a transaction. When you read without using an explicit transaction, the transaction will be setup for you. When you read after a transaction is committed, the behavior depends on NH configuration and driver.
Both ODBC and JDBC do not define what happens when a connection is closed and there's uncommitted or unrollbacked data. When the connection is reopened, it is possible that a new transaction is automatically started.
Using non-transactional access can only be used together with setting auto-commit
explicitly in the NHibernate configuration. If not, the default of the driver is used and it may work, or may not work.
In short, there are many shortcomings and undefined behaviors when you do not use transactions on reads. It will work often, but this depends on the configuration, the applied patterns, the drivers. There's a large chance you get LazyInitializationException
s, which is a common result of read after commit without opening a new transaction.
The "best practice" is to use one transaction for read/write and one other for read-only. This is described briefly in the previous link, section "Can I use two transactions in a session" but requires more of your implementation.
It is not only "use transactions for read", it is also: "use the same transaction you use for writing for reading". (and then, while this is true, actual application will depend on your current patterns, how many tiers there are, caching and configuration).
Update: expanded a bit, removed some ambiguities