I have read some post about fetch=join - http://nhforge.org/blogs/nhibernate/archive/2009/04/09/nhibernate-mapping-lt-many-to-one-gt.aspx (ser4ik.livejournal.com/2505.html)
So I have some question, Forexample I have class
<class name="AttributesInf" table="attr_inf">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Name" />
<property name="Desc"/>
</class>
and
<class name="AttributeValue" table="attr_val">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Value" />
<many-to-one name="AttrName" column="attrId"/>
</class>
If I use this mapping without set fetch="join" I get sql:
Select av.Id, av.Value, av.attrId From attr_val av where av.Id=...()
and after then separate sql queries like:
Select * From attr_inf where Id = av.attrId
So my Result Is:
class AttrinuteInf
{
int Id;
string Name;
string Desc;
}
class AttributeValue
{
int Id;
string Value;
AttributeInf AttrName;
}
If I set fetch="join" then I get one query:
Select u.Id, u.UserName, u.BlogId, b.Id, b.BlogName, b.BlogAuthor, b.BlogMsg
from Users u
Left outer join Blogs b
On u.BlogId=b.Id
Where u.Id = ...
So I expect to get one Class:
class AttributeValue
{
int Id;
string Value;
string Name;
string Desc;
}
But I have the same result as if I not set fetch to "join".
Is this all right?
Are there any way to get properties from class maped as <many-to-one> directly?
(not as AttrName.Name, but simply Name )
Explanation:
Part of mapping set above don't show my real problem.
I want to map some entity as IDictionary<string,AttributeValue>.
I map it as
<map name="Attributes" table="attr_val" lazy="true" cascade="all-delete-orphan" inverse="true">
<key column="item_id"></key>
<index column="name"></index> //I don't have that item in class AttributeValue, that is why I try to get it from other table
<one-to-many class="AttributeValue"/>
</map>
This isn't doing what you think it's doing. Using fetch=join is just there to eager load the many side of the relationship. In both cases you end up with the same objects returned. By default NHibernate will lazy load the related entities (which is why you get the second query). By using fetch=join you are asking for the entire object relationship at once but it will still populate the objects the same as without the fetch=join.
It's not the way you describe it. Your entities do not change depending on what you request.
You will obtain a list of instances of your main entity, with the association to the other being fetched. So if, in your code, you access the association, you will find the values.
If you don't fetch it, you wouldn't be able to access those fields, as they would not have been retrieved from the database.
I don't understand your question.
The 'fetch-join' attribute just defines how the sql that NHibernate generates to retrieve instances of your classes, should look like.
It has nothing to do with 'what' will be returned. NHibernate will translate the recordset (that is the result of the query) to correct instances of your classes.
If you just want to retrieve parts of your entity (like the name for instance), then you'll have to write a HQL query, or use the ICriteria API and maybe use projections.
Related
I'm struggling with a NHibernate related problem where I could use some input.
Introduction:
I have a legacy database where the relational concepts have not really been applied.
In the database I have an OrderLine table which contains data for an order lines.
On top of that the table also contains all columns with Order specific information. This could for example be order number of a customer.
E.x. If i have 10 order lines - then I have 10 rows in my OrderLines table and each row has all the Order specific data e.g. order number or customer information.
I did not want to have the above structure in my code so a view was created for Orders so that I could map my Order in NHibernate which then has a set/bag of OrderLines which makes much more sense.
Mapping: (simplified)
<class name="Order" table="[view_Orders]">
<bag name="OrderLines">
</class>
<class name="OrderLine" table="OrderLines" />
The problem:
The complexity of the view makes it impossible to save to the view. When trying NHibernates throws this exception:
NHibernate.Exceptions.GenericADOException: could not insert: XXX ---> System.Data.SqlClient.SqlException: View or function 'view_Orders' is not updatable because the modification affects multiple base tables.
My NHibernate mapping is constructed as an Order object which has a "set or bag" of OrderLine objects. Ideally I would like NHibernate only to persist the set of OrderLine objects instead of the whole object.
Is there a way of achieving this? I have tried locking the object using different lock modes but it did not help me.
You can use mutable="false" to avoid the update and deletes as this article says:
Immutable classes, mutable="false", may not be updated or deleted by the application. This allows NHibernate to make some minor performance optimizations.
To avoid the insert you can use the following statement (Uses the proyection instead an insert command, dont forget use check="none"):
<sql-insert check="none">SELECT 1</sql-insert>
Here is a tested example:
<class name="Order" table="[view_Orders]" mutable="false">
<id name="OrderId" type="System.Guid">
<generator class="guid.comb"/> <!-- Change as you need -->
</id>
<!-- Other properties -->
<!-- <property name="GrandTotal"/> -->
<set name="OrderLines" lazy="true" inverse="true" cascade="all-delete-orphan">
<key column="OrderId"/>
<one-to-many class="OrderLine"/>
</set>
<sql-insert check="none">SELECT 1</sql-insert>
</class>
<class name="OrderLine" table="OrderLine">
<id name="OrderLineId" type="System.Guid">
<generator class="guid.comb"/> <!-- Change as you need -->
</id>
<!-- Other properties -->
<!-- <property name="OrderId"/>
<property name="GrandTotal"/>/> -->
</class>
In case I do understand your issue, the solution is surprisingly simple. We just would mark root object with dynamic-update="true"
<class name="Order" table="[view_Orders]" dynamic-update="true">
...
</class>
And then apply update="false" to every property or reference which we have in that Order class mapped to view:
...
<property name="Code" update="false"/>
...
<many-to-one name="Country" update="false />
But our collection will need the standard, even cascade mapping:
<class name="Order" table="[view_Orders]" dynamic-update="true">
<bag name="OrderLines"
lazy="true"
inverse="true"
batch-size="25"
cascade="all-delete-orphan" >
...
</bag>
... // other stuff is update="false"
</class>
And now code like this would do management of OrderLines, while not executing any updates on the root object Order
var session = ... // get ISession
// load root
var root = session.Get<Order>(123);
// if needed change existing line (pretend there is one)
root.OrderLines[0].Amount = 100;
// add new
var newOrder = ... // new order
root.OrderLines.Add(newOrder);
session.Save(root);
session.Flush();
And that is it. Cascade on the root object is doing what we need, while the update="false" is not updating it...
NOTE: Just interesting note - there is also class and collection
setting mutable="false", but it would not work here... as the
solution mentioned above (it is sad, because that would be more
elegant, but not working as expected...). See:
19.2.2. Strategy: read only
If your application needs to read but never modify instances of a persistent class, a read-only cache may be used. This is the simplest and best performing strategy. Its even perfectly safe for use in a cluster.
<class name="Eg.Immutable" mutable="false">
I know it has been asked for many times, i also have found a lot of answers on this website, but i just cannot get out this problem.
Can anyone help me with this piece of code?
Many thanks.
Here is my parent mapping file
<set name="ProductPictureList" table="[ProductPicture]" lazy="true" order-by="DateCreated" inverse="true" cascade="all-delete-orphan" >
<key column="ProductID"/>
<one-to-many class="ProductPicture"/>
</set>
Here is my child mapping file
<class name="ProductPicture" table="[ProductPicture]" lazy="true">
<id name="ProductPictureID">
<generator class="identity" />
</id>
<property name="ProductID" type="Int32"></property>
<property name="PictureName" type="String"></property>
<property name="DateCreated" type="DateTime"></property>
</class>
Here is my c# code
var item = _productRepository.Get(productID);
var productPictrue = item.ProductPictureList
.OfType<ProductPicture>()
.Where(x => x.ProductPictureID == productPictureID);
// reomve the finding item
var ok = item.ProductPictureList.Remove(productPictrue);
_productRepository.SaveOrUpdate(item);
ok is false value and this child object is still in my database.
Not 100% sure, but could be because you have defined ProductID as a property of ProductPicture, I assume this is the PK from the Product class. You don't need to add this again, it will be created by the relationship.
I'm not sure that your use of table="[ProductPicture]" in the set tag is right.
The one-to-many tag already establishes the link between ProductPictureList and ProductPicture.
I think the table attribute is generally for using a separate relationship table when modelling many-to-may relationships.
From nhibernate.info Doc:
table (optional - defaults to property name) the name of the
collection table (not used for one-to-many associations)
And:
A collection table is required for any collection of values and any
collection of references to other entities mapped as a many-to-many
association
I've been trying to interpret the answers to similar questions but haven't been able to make it work.
I have a list of activities, and each activity as a list of participants. Here are the mappings:
<class name="Activity" lazy="false">
<id name="ID">
<generator class="guid" />
</id>
<list name="Participants">
<key column="Activity" />
<index column="Ord" />
<many-to-many column="Contact" class="Model.Contact" />
</list>
<property name="Timestamp" />
</class>
<class name="Contact" lazy="false">
<id name="ID">
<generator class="guid" />
</id>
<property name="Name" />
</class>
I am currently retrieving the activities ten at a time with this criteria:
var crit = ModelSession.Current.CreateCriteria<Activity>()
.AddOrder(Order.Desc("Timestamp"))
.SetFirstResult(start)
.SetMaxResults(count);
Now I need to retrieve only those activities in which a particular person participated, in other words: where Contact.Name like '%some_name%'. In raw SQL I'd probably write something like this:
select * from Activity where ID in (select p.Activity from Participants p,
Contact c where p.Contact=c.ID and c.Name like '%some_name%')
Any idea how to do this, with HQL or ICriteria, in a way that lets me keep the paged results and ordering? Many thanks!
ChssPly76 response above wasn't quite correct — this is a unidirectional relationship, so there is no "activity" property on the contacts to project — but it got me on the right track.
The correct answer is actually much simpler (in its own answer so I can format it properly):
crit.CreateCriteria("Participants")
.Add(Expression.Like("Name", like));
Added to the criteria from the original question, this says "return a page of activities, ordered by timestamp, where a participant has this name".
Taken a step further, I really wanted any activity where the search term appeared in the list of participants OR in the description of the activity. In this case you really do need the subquery.
var subquery = DetachedCriteria.For<Activity>()
.SetProjection(Projections.Property("ID"))
.CreateCriteria("Participants")
.Add(Expression.Like("Name", search_term));
crit.Add(Expression.Or(
Subqueries.PropertyIn("ID", subquery),
Expression.Like("Description", search_term));
You just need to add an appropriate condition to your criteria:
crit.CreateAlias("Participants", "participant")
.Add( Expression.Like("participant.Name", "%some_name%") );
See Criteria associations chapter of NHibernate documentation for more details
Update You can use DetachedCriteria to specify your Participant conditions as a subquery:
DetachedCriteria subquery = DetachedCriteria.For(typeof(Participant))
.SetProjection(Projections.Property("activity"))
.Add( Expression.Like("name", "%some_name%") );
crit.add( Subqueries.GeAll("ID", subquery) ).List();
I'm having trouble with something that (I think) should be simple, but can't find any clear info.
In the scenario where I have three tables, describing a domain where a person can have more than one job:
Person - has PersonId, Name
Job - has JobId, JobName
PersonJob - has PersonId, JobId, YearsOfEmployment
Note: In my object model, I have entities representing each table. I have that third entity to represent the Person/Job relationship since there is useful metadata there (YearsOfEmployment) and is not just a simple join table.
So, if I knew the PersonId and the JobId, is there a simple way for me to use the session and return an object matching those Ids?
Or, put a different way, since I already know the primary keys is there a brain-dead, simple way I can turn the SQL "SELECT YearsOfEmployment FROM PersonJob WHERE PersonId=1 AND JobId=1" into something like:
var keys = new {PersonId=1, JobId=2};
PersonJob obj = Session.Get<PersonJob>(keys);
BTW: maps would look something like this:
<class name="Person" table="dbo.Person" lazy="true">
<id name="PersonId">
<generator class="native"/>
</id>
<property name="Name"/>
</class>
<class name="Job" table="dbo.Job" lazy="true">
<id name="JobId">
<generator class="native"/>
</id>
<property name="JobName"/>
</class>
<class name="PersonJob" table="dbo.PersonJob" lazy="true">
<composite-id>
<key-property name="PersonId"></key-property>
<key-property name="JobId"></key-property>
</composite-id>
<property name="YearsOfEmployment"/>
</class>
Well, I answered my own question. I think posting your problem is almost as cathartic as talking it out with someone. If I were to make the composite-id of PersonJob a component or class, i.e.
<class name="PersonJob" table="dbo.PersonJob" lazy="true">
<composite-id name="PersonJobKey" class="PersonJobKey">
<key-property name="PersonId"></key-property>
<key-property name="JobId"></key-property>
</composite-id>
</class>
Then I can simply do this:
PersonJobKey key = new PersonJobKey() { PersonId = 1, JobId = 1 };
PersonJob obj = Session.Get<PersonJob>(key);
int yearsOfEmployment = obj.YearsOfEmployment;
cool. hope this helps anyone else figuring this out ...
Thanks for posting the answer above, I was looking at it against an object I have mapped where the composite key doesn't have a name or class. When I tried making a class to represent the composite key, it changed the way that the object behaved when used by other code. Also I wanted to write something like;
Session.Get<SalesRepArea>(new { AreaCode = "ACode", RegionCode = "RCode"});
I found that nHibernate couldn't make much sense of the anonymous object, but I did realise that I don't need a name for my composite key, or a class type. What the nHibernate Get method is after, in fact, is a transient object so that it can get it's equivalent object from the database (must be why you have to override the equals method in your C# class to get the composite key to work). So for the following map
<class name="SalesRepArea">
<composite-id>
<key-property
name="AreaCode" column="AreaCode" type="String" length="12" />
<key-property
name="RegionCode" column="RegionCode" type="String" length="12" />
</composite-id>
I write a bit less code, and dispense with the object representing the key to get
SalesRepArea myArea = Session.Get<SalesRepArea>(
new SalesRepArea()
{
AreaCode = "ACode",
RegionCode = "RCode"
}
);
I'm not saying that the named key method is bad, less code is not always better, it's just to show that Hibernate is looking for the object that the key is in to get the specific object from the database.
If I've got it wrong please let me know, but I hope this helps, as I was having a bit of trouble with this.
Thanks,
Mark
Mapping a dictionary with NH. Declaration is as follows:
<hibernate-mapping ...
<map
name="CostsByRole"
table="JobAccountingSnapshotCosts"
lazy="false"
fetch="join"
access="nosetter.camelcase-underscore">
<key column="SnapshotId" />
<index column="RoleCode" type="String" />
<element column="Amount" type="Decimal" />
</map>
</hibernate-mapping>
I am expecting a single SQL query to be generated but instead I get two: a select for the actual object, followed by a select for the contents of the dictionary.
Any ideas?
HQL queries do not consider the values set for fetch in the mapping. You need to specify them exclusively in each HQL query. Its supposedly by design. The fetch attributes value is used only by Criteria queries and by Load/Get.
Assuming it's not a typo on submission, the problem is likely to be the join="fetch" part in your mapping. It should be fetch="join" and since the default for fetch is "select", that would yield your sequential select problem.