NHibernate not persisting collections - nhibernate

I have a rather strange error with NHibernate. I am was having error with ISession been shared by across threads and got this resolved by supplying my own ADO.NET connection like:
IDbConnection connection = new SqlConnection(ApplicationConfiguration.ConnectionString);
connection.Open();
ISession session = _sessionFactory.OpenSession(connection);
session.FlushMode = FlushMode.Commit;
return session;
My application now works but all objects with collections are been persisted in the database without their collections. for example, say a car has a list of tyres. then i create a car and then generate a list of tyres based on tyres already in the database. saving the car object will only save the car not the list!
any help on what i am doing wrong? i am using NHibernate 2.0 an i do call Session.Flush() and Transaction.Commit().
cheers.

hi I figured out the reason why the collections were not been persisted. my unit of work was invoking a property which returned an Isession object to persist my objects. However, this property actually returned a new ISession for each call. COnce i corrected this to use the same ISession within each unit of work, the objects were persisted properly. Thanks for all your help though.

Check out the cascade attribute on your collection mapping - by default this is set to 'none', meaning child entities need to be explicitly saved. You probably want cascade="all" or cascade="all-delete-orphan".

are you using NHibernate.ISession.save(object) before the flush and commit of the tyres list?

Related

Nhibernate. When i call save(entity) it saves all my entities change to the DB

I have a vb.net app that allows users to change properties of too many objects but they can decide what to save or simply save them all. My problem is that when i change properties of a couple of objects and save one of them using this code:
Public Sub Add(ByRef entity As T) Implements Interfaces.ICrud(Of T).Add
Dim session As ISession = _conection.GetSesion()
Dim transaction As ITransaction = sesion.BeginTransaction()
session.Save(entity)
transaction.Commit()
End Sub
The other objects persists their changes to the DB too, and i haven't call save with any of them.
What am i doing wrong?
The NHibernate's ISession maintains the identity map of the objects associated to this session (loaded, added, etc). This concept is called level-one cache.
All objects associated to a session will be saved when flush even will happen.
hazzik is right.
Flush will call an update from your associated entities if them were changed (became dirty). I'm not sure if is the best option, but you can avoid this using session.Evict(entity) on all objects where you don't need an update.
Read more about dirty objects here and here.

JPA 2.0 and ActiveMQ : Lazy loaded entity to send through object message

When I load an entity which contains lazy-loading fields and I want to send this entity through an object message with ActiveMQ, will I receive :
The full entity (with lazy-loading fields loaded)
OR
The entity as sent (without lazy-loading fields loaded) ?
In any case do I need to put the Serializable marker in my entity ?
In case of answer 1 what do I need to do to get the entity as described in answer 2 ?
Lets assume you have an entity with some lazy loading fields like this:
#Entity
public class Foo
{
#Id
private Long id;
#OneToMany
private List<Bar> bars;
}
When you receive this entity from your database only the id field will be loaded as the bars field is lazy loaded (OneToMany is lazy by default). Now when you pass this entity through JMS for example the bars field is not initialized because the getBars() method isn't called. When the remote end calls the getter it will get a LazyInitializationException as the entity is detached and the collection isn't initialized.
If you however want the remote end to be able to call the getBars() method you need to initialize the collection in some way. You can make the fetching of the collection eager using #OneToMany(fetch = FetchType.EAGER). An alternative is to use a separate query for eager loading the entity (which is my personal preference).
Example:
select f from Foo f join fetch l.bars
If you really want to go wild and want the collection to be initialized lazily on the remote end you can take a look at Hibernate remote lazy loading using RMI. I'm not going to explain this because it seems out of the scope of the question. ;-)

LINQ functions and DataContext disposal, deferred execution

So I need some advice and insight here. Thanks in advance for your thoughts.
I have developed static functions that return a single record from a LINQ entity. Like so:
FooRecord GetRecord(Guid id)
{
using(var dc = new FooDataContext())
return dc.FooRecords.Where(a => a.Id == id).First();
}
This throws an exception because the DataContext is already disposed, which creates problems with deferred execution. This works:
FooRecord GetRecord(Guid id)
{
var dc = new FooDataContext();
return dc.FooRecords.Where(a => a.Id == id).First();
}
I am worried. How quickly will the DataContext be disposed? Obviously if I grab the record immediately this won't cause an issue. However, say I need to grab a record through association:
var record = Data.FooRecord.GetRecord(id);
//Do a bunch of stuff...
//Now we grab the related record from another entity
var barRecord = record.BarRecord
Is there a risk the DataContext be gone by this point? Any advice?
You basically do not need to Dispose() your DataContext for the reasons discussed here:
When should I dispose of a data context
http://csharpindepth.com/ViewNote.aspx?NoteID=89
The main reason for implementing IDisposable on a type is to dispose of any unmanaged resources. The only unmanaged resource allocated by the DataContext is the underlying database connection, but the DataContext already takes care of opening and closing the connection as needed.
The main thing you want to avoid is returning an IEnumerable collection and then never enumerating it, as this will cause the connection to remain open indefinitely. However, since you are only returning a single object, you shouldn't have to worry about this.
Also note that if access any relationship property on the returned object it may cause the connection to be momentarily reopened so that the property can be lazy loaded. You can avoid this by using DataLoadOptions.LoadWith() with your DataContext to eager-load any properties you intend to access. See http://msdn.microsoft.com/en-us/library/system.data.linq.dataloadoptions.aspx
As to the last part of the question, if the returned entities contain properties that can be lazy loaded, then they will contain internal references to back the DataContext that will keep it in memory. Once you have no more references to these entities, then the DataContext will of course be garbage-collected just like any other object.

CF9 ORM Populating an entity with an object

I am using Model-Glue/Coldspring for a new application and I thought I would throw CF9 ORM into the mix.
The only issue I am having right now is with populating an entity with an object. More or less the code below verifies that only one username can exist. There is some other logic that is not displayed.
My first thought was to using something like this:
var entity = entityload('UserAccount' ,{UserName=arguments.UserAccount.getUserName()},"true")
entity = arguments.UserAccount;
How ever this does not work the way that I expected. Is it even possible to populate an entity with an object or do I need to use the setters?
Not sure if this is what you're looking for. If you have...
component persistent="true" entityName="Foo"
{
property a;
property b;
}
You can pass a struct in the 2nd param to init the entity (added in CF9.0.1 I believe)
EntityNew("Foo", {a="1",b="2"});
To populate Foo with another object, you can use the Memento pattern, and implement a GetMemento() function to your object that returns a struct of all its properties.
EntityNew("Foo", bar.getMemento());
However, CF does NOT call your custom setters! If you want to set them using setters, you may add calls to the setters in your init() constructor, or use your MVC framework of choice to populate the bean. In Model-Glue, it is makeEventBean().
Update: Or... Here's hack...
EntityNew("Foo", DeserializeJSON(SerializeJSON(valueObject)));
Use this at your own risk. JSON might do weird things to your numbers and the 'yes','no','true','false' strings. :)
Is it even possible to populate an entity with an object or do I need to use the setters?
If you mean "Is it possible to create load an ORM Entity from an instance of that persistent CFC that already exists and has properties set?", then yes you can using EntityLoadByExample( object,[unique] )
entity = EntityLoadByExample( arguments.userAccount,true );
This assumes the userAccount CFC has been defined as persistent, and its username value has been set before being passed in (which seems to be the case in your situation).
Bear in mind that if any other properties have been set in the object you are passing, including empty strings, they will be used as filters to load the entity, so if they do not exactly match a record in your database, nothing will be loaded.

NHibernate evict by id

Everyone knows that there is cache in session.
This cache generally could be cleared by 2 methods:
Session.Evict
Session.Clear
Second method removes all cache not only for single entry.
I have business method. It receives id of large object (from aspx site) or sometimes several ids. And do native sql operation in database (using sql-query with complex logic to not load all data in C#). Then I need to invalidate cache. So every potential load of object goes without cache direct from database.
Unfortunately evict accepting only objects. Also its implementation DefaultEvictEventListener has clear separation in code path - separate for proxy and not proxied classes. I have tried simply to create entity, fill id manually and pass it to Evict. This will not works. As I understand Evict by not proxied class use GetHashCode to find and remove object from cache. So if I am not overriding it it will not works. I have a lot native sql batch operations so overriding all GetHashcode in all entities objects will create a lot of work. Also I am not sure is this case removes proxies from cache or no.
Update: As far as I have tried for me overriding GetHashCode also not helped. StatefulPersistenceContext.RemoveEntry not found entity because it uses RuntimeHelpers.GetHashCode. So this solution is not even possible
Using sources of NHibernate I have produced following solution:
public static class NHSessionHelper: DefaultEvictEventListener
public static void RemoveEntityFromCache(this ISession session, Type type, object entityId)
{
ISessionImplementor sessionImpl = session.GetSessionImplementation();
IPersistenceContext persistenceContext = sessionImpl.PersistenceContext;
IEntityPersister persister = sessionImpl.Factory.GetEntityPersister(type.FullName);
if (persister == null)
{
return;
}
EntityKey key = new EntityKey(entityId, persister, sessionImpl.EntityMode);
persistenceContext.RemoveProxy(key);
object entity = persistenceContext.RemoveEntity(key);
if (entity != null)
{
EntityEntry e = persistenceContext.RemoveEntry(entity);
DoEvict(entity, key, e.Persister, (IEventSource)sessionImpl);
}
}
It just uses part of NHibenate implementation. But it seem to me not good idea to duplicate code. May be anyone have other ideas?
If you are sure that the object is in the cache, Session.Get(id) will not hit the db. It's probably easiest to do that and then Evict the object you get back:
Model m = Session.Get(id);
Session.Evict(m);
Edit
It is not clear to me if you are talking about the first-level cache or the second-level cache. The above will evict something from the first-level cache. To evict from the second-level cache, use the evict method on SessionFactory.
Edit in response to comment
In that case, you might try Session.Load:
Model m = Session.Load(id);
Session.Evict(m);
If m is in the cache, Session.Load will return the instance which you can evict. If not it returns a proxy (no db hit). My tests suggest that Session.Evict will not throw if you try to evict the proxy, so this should work.
It sounds like you could use a Stateless session for this and not bother with cache at all.