Fluent Nhibernate, References and too many queries - fluent-nhibernate

When I add a Reference in my map the SQL generated fetches the foreign object with an outer left join. This gives good performance with only 1 SQL.
But when the referenced object does not exists on N rows NHibernate generates N extra queries to look up the non existing row. It should know it doesn't exists from the first query.
How do I disable this behavior?
References(x => x.Customer)
.Columns("c1", "c2")
.NotFound.Ignore();

Its a feature not a bug! https://nhibernate.jira.com/browse/NH-1001

Related

Nhibernate Fluent Mapping multiple table to one object

I have 3 tables that represents a many to many mapping.
Two tables with different ids and a third table with a composite key referencing the other two.
How can i map this using the classmap in nhibernate?
The following doesn´t work:
HasManyToMany(m =>
m.ListBlockStatus)
.Table("BlockTypeAction")
.ParentKeyColumn("IdBlockActionDefinition")
.ChildKeyColumn("IdBlockTypeCategory")
.Table("BlockTypeCategory")
.ParentKeyColumn("Id");
Found what i need, unfortunately the query in the end is not an inner join.
HasManyToMany(m => m.ListBlockStatus)
.Table("BlockTypeAction")
.ChildKeyColumns.Add("IdBlockActionDefinition")
.ParentKeyColumn("IdBlockTypeCategory")
.Cascade.All();

NHibernate one to many with LazyLoad off result in n+1 query

i am trying to turn off lazyloading for one-to-many mapping in NHibernate. I have the follow mapping in my entity mapping class. An entity has many addresses, and what I was looking for is one query that basically join the base table to the Addresses table and return me all the result in one request. Instead I see a series of sql query submitted to database for each record in the base table.
HasMany(m => m.Addresses).Not.LazyLoad().Fetch.Join();
i need a way to turn off lazyloading completely.
I would strongly suggest to read this blog post by Ayende: NHibernate is lazy, just live with it.
Using ORM and trying to avoid laziness... won't work. In case of addresses you will lose paging for example.
(while fetching them via join, what happens? If there will be entity with 10
addresses, and you will ask for first 10 records... you will get just
one. And it could be worse if you will ask for 11...)
But what you can use, is the power of NHibernate: 19.1.5. Using batch fetching
HasMany(m => m.Address)
...
.Fetch.Select()
.BatchSize(25)
Now, if you will need 25 records, there will be 2 SELECTs. First for entity, second for all the related Addresses. That's improvement, while all the advantages of ORM remain.
I think that would be anough:
HasMany(m => m.Addresses).Not.LazyLoad();
To get the data with select you should explicitly use "Fetch":
session.QueryOver<Item>()
.Fetch(item => item.Addresses).Eager
.Take(1000)
.TransformUsing(Transformers.DistinctRootEntity)
.List();
I assume you load the base entity with a HQL, Linq or plain SQL query. Those queries ignore the "join" fetch settings in the mapping. You have to either explicitly fetch the Adresses in the query or use Get/Criteria/QueryOver.
Reference documentation: http://nhibernate.info/doc/nh/en/index.html#performance-fetching-custom

NHibernate - how to configure associations not to use primary key

I'm working with a legacy database put together by some very strange people. I'm writing an NHibernate DAL over the top of it but running into some odd mapping scenarios.
In one example, I have a table with a number of fields including LE_RECNUM (integer primary key) and LE_CODE (string).
However, all relationships throughout the database join onto LE_CODE, not LE_RECNUM, for some unfathomable reason.
I need to specify that LE_RECNUM is the Id in my mapping file, because I need the primary key to be generated when I insert records. However, I want all associations to use LE_CODE instead of LE_RECNUM.
Can anyone suggest how I might do this?
References(x => x.SomeProperty).Column("LE_CODE").PropertyRef(x => x.SomePropertyInParent);

NHibernate double fetching for non-existent many-to-one relationship

I have an entity with several Many to One relationships and I've found I can eagerly fetch them in one query like such:
public Accommodation GetEager(int id)
{
IList<Accommodation> query = NHibernateSession.CurrentFor(NHibernateSession.DefaultFactoryKey)
.CreateQuery(#"
select a from Accommodation as a
left join fetch a.AccommodationType
left join fetch a.AccommodationUnitType
left join fetch a.CollectionType
where a.Id = :id
")
.SetProperties(new {id})
.SetCacheable(true)
.List<Accommodation>();
return query.SingleOrDefault();
}
However, the relationships don't always exist, and I've defined the mappings like so:
mapping.References(x => x.AccommodationUnitType).NotFound.Ignore();
I've found that when a relationship doesn't exist, NHibernate generated a second SQL query looking for it, presumably because it's found that the property is null.
My first question is, how can I prevent this second sql query?
My second question is, is there an easier way to do this fetching into one query? It seems very basic behaviour that one would want to fetch everything in one query rather than a seperate query for each many-to-one relationship (which seems to be the default behaviour).
Are you sure you're using NotFound().Ignore() correctly? This setting determines what NHibernate will do if there is an invalid foreign key. In that case NotFound().Ignore() prevents throwing an EntityNotFoundException. With the setting, if the related object is not found then the value will be null. If you have referential integrity for this relationship then you do not need NotFound().Ignore().
The behavior you're seeing is apparently expected and well known and unlikely to change.
As for your second question, I would advise always starting with the default behavior of lazy loading and only optimizing with eager loads as needed for real world performance problems. Lazy loading is frequently more efficient than eager fetching because a) the object may already be in cache (no db trip required) and b) selecting by primary key is very fast. It's very possible that your query with three joins performs worse than four selects by primary key.

Can I use nHibernate with a legacy-database with no referential-integrity?

If I have a legacy database with no referential-integrity or keys and it uses stored procedures for all external access is there any point in using nHibernate to persist entities (object-graphs)?
Plus, the SP's not only contain CRUD operations but business logic as well...
I'm starting to think sticking with a custom ado.net DAL would be easier :(
Cheers
Ollie
You most likely CAN. But you probably shouldn't :-)
Hibernate does not care about referential integrity per se; while it obviously needs to have some sort of link between associated tables, it does not matter whether actual FK constraint exists. For example, if Product is mapped as many-to-one to Vendor, PRODUCTS table should have some sort of VENDOR_ID in it but it doesn't have to be a FK.
Depending on your SP signatures, you may or may not be able to use them as custom CRUD in your mappings; if SPs indeed have business logic in them that is applied during all CRUD operations, that may be your first potential problem.
Finally, if your SPs are indeed used for ALL CRUD operations (including all possible queries) it's probably just not worth it to try and introduce Hibernate to the mix - you'll gain pretty much nothing and you'll have a yet another layer to deal with.
okay, an example of the problem is this:
A SP uses a sql statement similar to the following to select the next Id to be inserted into the 'Id' column of a table (this column is just an int column but NOT an identity column),
statement: 'select #cus_id = max(id) + 1 from customers',
so once the next id is calculated it's inserted into table A with other data, then a row is inserted into table B where there is ref to table A (no foreign key constraint) on another column from table A, then finally a row is inserted into table C using the same ref to table A.
When I mapped this into NH using the fluent NH the map generated a correct 'insert' sql statement for the first table but when the second table was mapped as a 'Reference' an 'update' sql statement was generated, I was expecting to see an 'insert' statement...
Now the fact there is no identity columns, no keys and no referential-integrity means to me that I can't guarantee relationship are one-to-one, one-to-many etc...
If this is true, how can NH (fluent) configured either...
Cheers
Ollie