FluentHibernate: how to specify join-mapped table should never be inserted into - nhibernate

I have the following FluentNHibernate mapping:
public AssetMap()
{
Table("PRASSET");
Id(x => x.Id).Column("PRA_RECNUM").GeneratedBy.Sequence("PRA_RECNUM_GEN");
...fields mapped from PRASSET table
Join("PRSTOCK", m =>
{
m.Fetch.Join();
m.Optional();
m.KeyColumn("PRS_ASSRN");
...fields mapped from PRSTOCK table
});
}
As the m.Optional() call in the Join bit suggests, there may or may not be a corresponding PRSTOCK record.
The problem I am having is that when I save an Asset instance which is dirty only in respect of one of its PRASSET-mapped properties, and there isn't a corresponding PRSTOCK record, it's inserting one! This is emphatically not what I want to happen, since there should not be a corresponding PRSTOCK record in most cases.
Is there a tweak in the mapping configuration I can try? It would be enough at this stage just to prevent records ever being inserted into PRSTOCK, but ideally NHibernate should not try to update or insert PRSTOCK unless one of the Asset instance's properties mapped to PRSTOCK has been dirtied.

According to this blog post, NHibernate will always try to insert join-mapped records if the properties are not null. So I have made the appropriate properties nullable (default null) and hopefully it won't try to insert anymore.

Related

Nhibernate foreign key constraint due to unexpected update of already inserted record

This is a strange one replicated in the following code:
using (ISession session = RepositoryTestHelper.SessionFactory.OpenSession())
{
//session.BeginTransaction();
UserRepo repo = new UserRepo(session);
CompanyRepo cRepo = new CompanyRepo(session);
var user = repo.FindByEmail("test.user#blah.com");
user.CompanyAssociations.Add(new CompanyUserAssoc()
{
User = user,
Company = cRepo.GetById(1)
});
repo.AddOrUpdate(user);
//session.Transaction.Commit();
}
And the relationship between user, company and CompanyUserAssoc is fairly straight forward:
For the company:
HasMany<CompanyUserAssoc>(x => x.UserAssociations).KeyColumn("User_id");
For the user:
HasMany<CompanyUserAssoc>(x => x.CompanyAssociations).KeyColumn("Company_id")
And for the association class itself:
References(x => x.Company).UniqueKey("CompanyId_UserId");
References(x => x.User).UniqueKey("CompanyId_UserId");
Now this is where I am baffled. Notice in my initial code that the begin and commit trans calls are commented out. This actually means the code will work! The CompanyUserAssoc is created and correctly references the user and the company with id of 1. Great!
But... sadly when I put this in a transaction i get this error:
{"The UPDATE statement conflicted with the FOREIGN KEY constraint \"FK3C47859753A62C6E\". The conflict occurred in database \"xxxx\", table \"dbo.Company\", column 'Id'.\r\nThe statement has been terminated."}
But why? Well that's my question. What i have see in the profiler is that it does this:
exec sp_executesql N'UPDATE [CompanyUserAssoc] SET Company_id = null WHERE Company_id = #p0',N'#p0 int',#p0=1
Wait... what? NULL? Why is it setting the company id to null? and why is it only doing this when in a transaction? What's "wrong" with my Nhibernate mapping?
Since both collections are mapped as non-inverse (the default) and the Company collection has no users, it will issue that update to clear the link table. Could you try setting the UserAssociations to inverse? Or, what I usually do when an explicit link table is involved, is set both HasManys to inverse and simply work with the link table directly.
This was a red herring in the end, a fish I have come to despise. I had not noticed in my haste and dependence on intellisense that the UserAssocations property of the Company was not an IList but an IList! yes I have stupidly chosen the Nhib map class instead of the domain class.. This weirdly didn't error in the way you would expect, and worked without a transaction, but why?
Well - Nhib was able to insert the assoc entry, but then believed it needed to update that same table with the id it had already inserted (because it somehow sees it as a different entity?). I would have expected a compilation error here in the map class itself as I was using the type, but no. And without a transaction the sql generated works.
I would investigate more as to why, but I've answered this just to highlight a rare and silly mistake which can lead to a lot of investigation if missed.

Invalid index N for this SqlParameterCollection with Count=N only when associated table has null record

I have a rather complex entity which will not save when a particular database table is missing a record. When the record exists the entity saves correctly. When the record does not I receive the exception:
Invalid index N for this SqlParameterCollection with Count=N
After reading a bunch of solutions found via Google and the most closely related questions on Stack Overflow:
What's causing “Invalid index nn for this SqlParameterCollection
with Count=nn” when a column is Null in the database?
Invalid Index n for this SqlParameterCollection with Count=n” OR “foreign key
cannot be null
I believe my issue has to do with the way I have my mapping files setup. The Customer entity has reference to the Person entity. Person maps to a table which we have read, but not write access to. It is when a record for the Person entity does not exist that I generate the exception. If the record exists no issue. I've set the reference to Person from customer to Nullable(). I have also double checked to ensure I do not have a property mapped twice from either entity.
Here is what I feel is the pertinent mapping information, but can provide more as needed:
Customer
//more mapping code...
References(x => x.Person, "snl_id").Nullable();
//more mapping code...
Person
//more mapping code...
ReadOnly();
Id(x => x.SnlId).Column("SNL_ID");
//more mapping code...
To further complicate matters we have some painful code to make NHibernate perform better when Person does not exist. I am not sure it applies here, but thought it pertinent enough to include in my question. We are using the below code because without it the NHibernate JIRA will create tons of queries. This solution is outlined in this Stack Overflow answer.
Customer's person property
public virtual Person Person
{
get
{
try
{
var snlId = per.Name;
return per;
}
catch
{
return null;
}
}
set
{
per = value;
}
}
private EPerson per;
What am I missing in my mappings that would cause this exception? Is there another piece of this problem that I am not seeing?
While Scott's solution of removing the snl_id property from the Customer class fixes the issue it causes problems that I cannot get around-- the snl_id can exist in the Customer table even there is not a corresponding Person table record. Since that is the case there are times when I will need access to the snl_id when I cannot get to it via the associated Person property.
I considered several alternative solutions but settled on creating a view of the Customer table including the Customer table primary key and the snl_id from the customer table. Then mapping that property via a join to the view.
Join("v_cust_id_snl_id", j => j.KeyColumn("cust_id").Map(x => x.SnlId, "snl_id")
This change allowed me to have my cake and eat it to. I was able to keep the SnlId property on customer, but no longer throw the exception when saving.
Do you have the snl_id referenced as a property in Customer as well as being the primary key for the child object? If so, this is causing the error you are receiving. Remove the property from Customer and use Person to get the value.

nhibernate - sproutcore : How to only retrieve reference ID's and not load the reference/relation?

I use as a front-end sproutcore, and as back-end an nhibernate driven openrasta REST solution.
In sproutcore, references are actualy ID's / guid's. So an Address entity in the Sproutcore model could be:
// sproutcore code
App.Address = App.Base.extend(
street: SC.Record.attr(String, { defaultValue: "" }),
houseNumber: SC.Record.attr(String),
city: SC.Record.toOne('Funda.City')
);
with test data:
Funda.Address.FIXTURES = [
{ guid: "1",
street: "MyHomeStreet",
houseNumber: "34",
city: "6"
}
]
Here you see that the reference city has a value of 6. When, at some point in your program, you want to use that reference, it is done by:
myAddress.Get("city").MyCityName
So, Sproutcore automatically uses the supplied ID in a REST Get, and retrieves the needed record. If the record is available in de local memory of the client (previously loaded), then no round trip is made to the server, otherwise a http get is done for that ID : "http://servername/city/6". Very nice.
Nhibernate (mapped using fluent-nhibernate):
public AddressMap()
{
Schema(Config.ConfigElement("nh_default_schema", "Funda"));
Not.LazyLoad();
//Cache.ReadWrite();
Id(x => x.guid).Unique().GeneratedBy.Identity();
Table("Address");
Map(x => x.street);
Map(x => x.houseNumber);
References(x => x.city,
"cityID").LazyLoad().ForeignKey("fk_Address_cityID_City_guid");
}
Here i specified the foreign key, and to map "cityID" on the database table. It works ok.
BUT (and these are my questions for the guru's):
You can specify to lazy load / eager load a reference (city). Off course you do not want to eager load all your references. SO generally your tied to lazy loading.
But when Openrast (or WCF or ...) serializes such an object, it iterates the properties, which causes all the get's of the properties to be fired, which causes all of the references to be lazy loaded.
SO if your entity has 5 references, 1 query for the base object, and 5 for the references will be done. You might better be off with eager loading then ....
This sucks... Or am i wrong?
As i showed how the model inside sproutcore works, i only want the ID's of the references. So i Don't want eagerloading, and also not lazy loading.
just a "Get * from Address where ID = %" and get that mapped to my Address entity.
THen i also have the ID's of the references which pleases Sproutcore and me (no loading of unneeded references). But.... can NHibernate map the ID's of the references only?
And can i later indicate nHibernate to fully load the reference?
One approach could be (but is not a nice one) to load all reference EAGER (with join) (what a waste of resources.. i know) and in my Sever-side Address entity:
// Note: NOT mapped as Datamember, is NOT serialized!
public virtual City city { get; set; }
Int32 _cityID;
[Datamember]
public virtual Int32 cityID
{
get
{
if (city != null)
return city .guid;
else
return _cityID;
}
set
{
if (city!= null && city.guid != value)
{
city= null;
_cityID = value;
}
else if (city == null)
{
_cityID = value;
}
}
}
So i get my ID property for Sproutcore, but on the downside all references are loaded.
A better idea for me???
nHibernate-to-linq
3a. I want to get my address without their references (but preferably with their id's)
Dao myDao = new Dao();
from p in myDao.All()
select p;
If cities are lazy loading in my mapping, how can i specify in the linq query that i want it also to include my city id only?
3b.
I want to get addresses with my cities loaded in 1 query: (which are mapped as lazyloaded)
Dao myDao = new Dao();
from p in myDao.All()
join p.city ???????
select p;
My Main Question:
As argued earlier, with lazy loading, all references are lazy loaded when serializing entities. How can I prevent this, and only get ID's of references in a more efficient way?
Thank you very much for reading, and hopefully you can help me and others with the same questions. Kind regards.
as a note you wrote you do this
myAddress.Get("city").MyCityName
when it should be
myAddress.get("city").get("MyCityName")
or
myAddress.getPath("city.MyCityName")
With that out of the way, I think your question is "How do I not load the city object until I want to?".
Assuming you are using datasources, you need to manage in your datasource when you request the city object. So in retrieveRecord in your datasource simply don't fire the request, and call dataSourceDidComplete with the appropriate arguments (look in the datasource.js file) so the city record is not in the BUSY state. You are basically telling the store the record was loaded, but you pass an empty hash, so the record has no data.
Of course the problem with this is at some point you will need to retrieve the record. You could define a global like App.WANTS_CITY and in retrieveRecords only do the retrieve when you want the city. You need to manage the value of that trigger; statecharts are a good place to do this.
Another part of your question was "How do I load a bunch of records at once, instead of one request for each record?"
Note on the datasource there is a method retrieveRecords. You can define your own implementation to this method, which would allow you to fetch any records you want -- that avoids N requests for N child records -- you can do them all in one request.
Finally, personally, I tend to write an API layer with methods like
getAddress
and
getCity
and invoke my API appropriately, when I actually want the objects. Part of this approach is I have a very light datasource -- I basically bail out of all the create/update/fetch methods depending on what my API layer handles. I use the pushRetrieve and related methods to update the store.
I do this because the store uses in datasources in a very rigid way. I like more flexibility; not all server APIs work in the same way.

Fluent NHibernate LazyLoad Issues

I couldn't find an answer to this issue so I assume it is something I am doing wrong.
I have a PersistenceModel set up where I have set a convention as follows: -
persistenceModel.Conventions.Add(DefaultLazy.Always());
However, for one of the HasManyToMany relationships in one of my entities I want eager loading to take place which I am setting up as follows: -
HasManyToMany(x => x.Affiliates).Not.LazyLoad();
Intuitively, I expect eager loading to take place as I am overriding the lazy load default that I have specified as a convention but it still lazy loads. If I set the DefaultLazy convention to never and then set LazyLoad on an individual relationship it doesn't work either.
Any ideas?
When you set Not.LazyLoad(), you tell NHibernate to load Affiliates when the parent loads. NHibernate will do this by performing another select on the Affliates many-to-many table regardless of whether you access the Affiliates collection or not. NHibernate is using another select because that is the default fetching mode. You want to override fetching mode as well, either in the query or in the mapping. To do it in the mapping, add the following:
HasManyToMany(x => x.Affiliates)
.Not.LazyLoad()
.Fetch.Join();
You might also want to include a ".Cascade.AllDeleteOrphan()" if you want NHibernate to persist new Affiliaites added to the collection and delete orphaned ones. If you do not do this, you will have to explicitly call session.Save(newAffiliate). Otherwise you'll receive a TransientObjectException when your Affiliates collection contains a new Affiliate.
It may be one stupid thing to ask, but have you execute the query inside your session? Say,
Using(var session = OpenSession())
{
session.Query<Entity>().ToList();
}
I had this problem before, and finally realized the objects that I was accessing hadn't been queried before disposing the session.

NHibernate - Incorrect thinking? Subclassed Model based on Join

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.