Let me explain the problem - hopefully I have defined it well in the title but I want to be sure.
I have a linq query that pulls back a bunch of objects (say Foos). Each Foo holds a reference to a User. Each User holds a reference to a Person:
public class Foo
{
//properties omitted...
public User CreatedBy {get;}
}
public class User
{
//properties omitted...
public Person Person {get;set;}
}
As the object structure would suggest, in the database, Foo relates many-to-one to User, and User relates many-to-one to Person.
When I run the query, I get a single SELECT for the Foos, then a SELECT each for all the Users and People. Clearly I would much prefer a single SELECT with a couple of joins.
I don't necessarily want to specify in my mapping config that Foos ALWAYS eager fetch the User, or that Users ALWAYS eager fetch the Person, but I would like to be able to specify that in this instance.
Is there a way to do that?
Thanks
David
All the NHibernate query methods have ways of specifying eager fetching.
For Criteria, you have SetFetchMode.
For HQL, you have [inner|left] join fetch.
For Linq yo have Expand (2.x contrib) / Fetch (3.x).
For SQL you have AddJoin.
Both Udi Dahan and Ritesh Rao offer example implementations of dynamic fetching strategies for NHibernate, this should give you a good starting point.
Additionally to Diegos nice answer: You can also use batching. This reduces the N+1 problem without much pain:
use batch-size on class level:
<class name="Person" batch-size="20">
...
</class>
use batch-size on collection level:
<map
name="SomeCollection"
batch-size="20">
...
</map>
When ever one of these references is loaded, NHibernate loads 20 at once using a query like this:
select ... from Person where user_fk in (23, 34, 6, 667, 6745, 234 ....)
So it turns N+1 to N / 20 + 1, which is pretty good.
Related
I have such domain structure (example, pseudo code):
class Room
{
Id: long
Shelves: list of Shelf
}
class Shelf
{
Id: long
Room: Room class (room_id column)
Books: list of Book
}
class Book
{
Id: long (id column)
Shelf: Shelf class (shelf_id column)
Room: Room class (room_id column)
Title: string (title column)
}
Every collection is lazy.
I'd like to know is there a way to fetch all books for a room (without subselect) when I access a Shelf's Books property in lazy manner.
When I get Room, nhibernate fetches only room (select * from rooms r where ...), when I access Shelves property, nhibernate fetches all shelves for this room (select * from shelves s where s.room_id = X), when I access Books property it loads books for the only one shelf (what is normal in general), but...
In my case if I access Book, there is high probability that I will work with other books of this room too, so if I touch book I want to preload all books for this room and put them on theirs shelves.
The most closest feature of nhibernate is fetch='subselect'. But there is unnecessary select in my case because I can fetch books for this room by room_id: select * from books b where b.room_id = x.
Is there a way to do this?
Thanks!
The native way built into NHibernate is called Batch Fetching:
19.1.5. Using batch fetching
NHibernate can make efficient use of batch fetching, that is, NHibernate can load several uninitialized proxies if one proxy is accessed (or collections. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can tune batch fetching: on the class and the collection level.
More details (except the doc link above) could be found here:
How to Eager Load Associations without duplication in NHibernate?
NHibernate Fetch/FetchMany duplication in resultset, how to fix with ToFuture()
How to eager load objects in a list/collection?
I've found a more elegant solution. It turns out I can define sql-query for a bag/map...
It looks like:
<hibernate-mapping...>
<class name="Shelf">
<many-to-one name="Room" column="room_id"... />
<bag name="Books">
...
<loader query-ref="ShelvesQuery" />
</bag>
</class>
<sql-query name="ShelvesQuery">
<load-collection alias="book" role="Shelf.Books" />
<![CDATA[
select b.id
b.room_id,
b.shelf_id,
b.title
from books b
where b.room_id = :room_id
]]>
</sql-query>
</hibernate-mapping>
It works like a charm!
Is it possible in myBatis 3 to map a single result to multiple objects, ensuring that the objects all reference the same instance? Is there an example of this I could reference?
Updated to add more detail:
For instance, let's say I store information regarding Contacts for my application in my DB. I want to know if it's possible to use myBatis to map the same instance of a contact to, say, a Listing class, which holds a Contact:
public class Listing {
private Contact myContact;
//getters & setters...
}
as well as to a ContactsHolder class, which also holds a Contact:
public class ContactsHolder {
private Contact aContact
//getters & setters...
}
I need the object that is mapped by myBatis to both the Listing and ContactsHolder classes to be the same instance. Is this possible?
No, MyBatis isn't able to do that with standard result mapping. (at least to my knowledge). You could select the "Contact" object, then build a Listing and ContactsHolder manually with both of them referencing the Contact.
Or implement a custom ResultSetHandler.
It's kind of a peculiar request, I'm not sure why you want the same instances shared across two objects like that. That's probably why no feature like this exists in MyBatis 3.
This can be done with standard result mapping if you use select to fetch the associated contact and both objects are fetched in the same session.
Modify result maps for Listing and ContactsHolder:
<resultMap type="Listing" id="listingMap">
<association property="myContact" column="contact_id" javaType="Contact" select="selectContact"/>
</resultMap>
<resultMap type="ContactsHolder" id="contactsHolderMap">
<association property="aContact" column="contact_id" javaType="Contact" select="selectContact"/>
</resultMap>
Now create a query selectContact:
<select id='selectContact' resultType='Contact'>
SELECT * from contact where id = #{id}
</select>
Now if you create some select that uses both listingMap and contactsHolderMap to map Listing and ContactsHolder records that reference the same contact they will both query for the contact using the same id.
Mybatis uses local cache for all objects read in a session so during fetching of the second associated contact the cache will be hit and the same object will be reused.
Even if you do two queries manually to get Listing and ContactsHolder in the same transaction the same Contact will be used in both returned objects.
I have this scenario:
class A
{
public virtual int Id { get; set; }
public virtual B Child { get; set; }
}
class B
{
public virtual int Id { get; set; }
}
In the mapping of class A, I have a reference to class B:
map.Reference(a => a.Child).LazyLoad();
Now when I do something like:
Session.Query<TypeOfA>().Select(a => a);
Apart from the normal select * from ATable I get n selects from the BTable for each A line. Is like lazy loading is not working.
My questions are:
How to I make the lazyload work here ?
Can I bring the A entities and B entities in a single query ?
Thank you,
Lazy loading is switched on by default and should actually work. If there would be a problem, for instance if it can't generate the proxy for class B, it would complain when creating the session factory.
Are you sure that the queries for B are done by the query itself, and not be subsequent access to A?
You could optimize the access to B in two ways: fetch them together with A in a single query. (I don't know fluent, this is the xml way to configure it:)
<many-to-one fetch="join" ...>
This has some problems when used with lists and could also blow up your query a lot. It is of course not lazy loading at all.
Another, very nice and powerful optimization is batch fetching. It allows the instances to be fetched in separate queries, but fetches several of them at once.
<class name="B" batch-size="20" ...>
This would fetch 20 B's at once in one query. It is also available for lists:
<one-to-many fetch-size="20" ...>
Expanding on Stafan's suggestion to use batch-size, a quick Google search reveals that Fluent NHibernate now supports BatchSize for queries. From the docs:
ClassMap<T> BatchSize(int size)
Sets the query batch size for this entity.
Never used it myself, and the documentation is minimal (like a lot of FNH), but maybe you can find some sample code.
I have the following model which I have created and mapped with nHibernate.
Using lazy loading so I don't need to get the Vehicles for the Dealer at the start.
Public class Dealer
{
public virtual string Name { get;set;}
public virtual IList<Vehicles> Vehicles { get;set;}
}
Now let's assume the Dealer has thousands of vehicles.
If I do Dealer.Vehicles.Count then NH will select and pull all the data.
What is the best way to simply get a count? Is there any way in which I can get a count with out declaring A new property dealerCount within the Dealer Class?
Also there is a feature in Hibernate which I believe will be implemented in a newer version of NH called Extra Lazy Loading. Would this solve the problem?
extra lazy loading would issue sql instead of populating the collection for certain operations such as Count or Contains. In fluent mappings its used as:
HasMany(x => x.CollectionProperty).ExtraLazyLoad();
or HBM
<one-to-many lazy="extra" ...
It's only usefull if you have large collections and need the special behavior.
Use count projection (Projections.RowCount)
I have a simple model class (Part), which pulls from it's information from a single table (t_Part).
I would like a subclass of this model called (ProducedPart), that would still utilize NHibernate's caching mechanisms, but would only be instances of (Part) that have a foreign key relationship in a table called "t_PartProduction". I do not need to have a model for this second table.
I only need a read-only version of ProducedPart
I could always implement a Facade/Repository over this, but I was hoping to setup a mapping that would pull "t_Part" joined with "PartProduction" when I asked for "ProducedPart" in NH.
Is this the wrong way to use NH?
Edit
So, the SQL would look something like
SELECT p.*
FROM t_Part p
INNER JOIN t_PartProduction pp ON pp.PartID = p.PartID
WHERE pp.ProductionYear = '2009'
I believe what you are looking for is a joined subclass. In FNH, it will look something like:
public class PartMap : ClassMap<Part>
{
public PartMap()
{
Id(x => x.Id)
JoinedSubClass<ProducedPart>("PartID", sub => {
sub.Map(x => x.Name);
sub.Map(x => x.ProductionYear);
});
}
}
In order have NHibernate cache the results, you will need to have the subclass mapped (and if you didn't map it, you wouldn't be able to get NH to load it in the first place).
Bringing in some context from the FNH groups thread, it will not explicitly be read-only though. In my opinion, making things read-only is not an appropriate thing for NHibernate to manage. This is better controlled by the database and connections (i.e. creating a connection to the database that only has SELECT permissions on the tables/views being accessed). See my answer to a previous SO question about readonly sessions in NHibernate for more of my thoughts on the matter.
The key here is using both the where and mutable elements of the class definition for NHibernate Mappings.
Using Fluent NHibernate, this looks like:
public Part()
{
WithTable("t_Part");
Id(i => i.Id).ColumnName("PartID");
Map(m => m.Name).ColumnName("Part");
SetAttribute("where", "PartID IN ( SELECT pp.PartID FROM t_PartProduction pp WHERE pp.ProductionYear = '2009' ) ");
ReadOnly();
}
No, this is perfectly possible. Look in the NHibernate documentation for the "table per subclass" model of inheritance. It will actually implement this as a LEFT JOIN, so that when you load a Part, it creates an instance of either your Part or your ProducedPart class depending on whether the other row is present. You'll find documentation on nhibernate.info.
I'm not sure you could make ProducedPart read-only doing this though.
I'm assuming from this:
WHERE pp.ProductionYear = '2009'
that you want the subclass only where the production year is 2009, i.e. if when there is a record in t_PartProduction for a different year, you want this Part treated as a plain Part object, not a ProducedPart, then you could consider creating a view definition within your database that is a filtered version of t_PartProduction, then making your subclass join to this view rather than the base table.