The session is the main interface in NHibernate, and it is also the main interface for our persistence and data loading, and ISession plays a coordinator between IclassPersister, ItransAction, Icriteria and iQuery.
The session object is obtained by calling the OpenSession method of the session factory. The OpenSession method has a parameter Interceptor. This is an interceptor that is completed by an object that implements the Iinterceptor interface. It is typically a log record for a session.
1. Status of persistent objects
The status of persistent objects is maintained by the EntityEntry Class.
Sealed INTERNAL CLASS EntityENTRY {
Private lockmode _lockmode;
Private status_status;
Private Object_ID;
Private object [] _loadedState;
Private object [] _deletedState;
Private bool_existsindatabase;
PRIVATE OBJECT_VERSION;
// for Convenience to save some lookups
[Nonserialized] private iClassPersister_Persister;
Private string _classname;
// ...
}
Private idictionary entitiesbykey; // key = key, value = Object
EntitiesbyKey Sets to save all persistent objects in the current session.
[Nonserialized] private ideTiTYMAP entries; // key = Object, value = Entry
ENTRIES Collection Maintains the status of all persistent objects in the current session, the items in the entry in Entries are one or one.
2. Persistence operation
When the persistence operation is performed (Save / Update / Delete), in addition to a few cases, the persistence operation is not executed immediately (updated the data source), but is recorded until the session flush will be updated to the data source, The reason for this is easy to understand, which is to avoid more annoying database connection operations. If you don't call Flush, the session is closed, the lasting object in the current session will not last!
There are three sets of sessionImpl.cs to record program objects to persist:
[Nonserialized] private arraylist insertions;
Record all ScheduledInsertion objects, the ScheduledInsertion object is created by a persistent object to SAVE, if the identity of the object must be obtained from the database (such as Identity ID), then the ScheduledInsertion object is not created, but immediately performs Save operation, the reason is simple Because you have to get the Identity ID;
[Nonserialized] Private ArrayList Updates; Record all ScheduledUpdate objects, the ScheduleDuPdate object is created by a persistent object to UPDATE;
[Nonserialized] Private ArrayList Deletions; Record all ScheduledDeletion objects, the ScheduledDeletion object is created by a persistent object to DELETE;
The above three planned objects are inherited from the ScheduledEntityAction object, and this object implements the Iexecutable interface, the EXECUTE method of the IExecutable interface is used to perform a long-term operation, which is indirectly called by Flush. Let's take a look at Flush's code: public void flush () {
IF (cascading> 0) Throw new hibernateException ("...");
Flusheverything ();
EXECUTE ();
Postflush ();
}
Execute performs all plan objects.
Private void execute () {
Log.debug ("Executing Flush");
Try {
Executeall (Insertions);
INSERTIONS.CLEAR ();
Executeall (Updates);
Updates.clear ();
// ...
Executeall (Deletions);
DETENS.CLEAR ();
}
Catch (Exception E) {
Throw new adoException ("...", e);
}
}
Execute an Insert / Update / Delete program separately.
Private void ExecuteAll (Icollection COLL) {
{Iexecutable E in Coll {
Executions.Add (e);
e.execute ();
}
IF (BATCHER! = null) Batcher.executebatch ();
}
3. Saveisessions There are two ways to hold persistent objects that distinguish whether there is a specified object ID (identifier).
Public Object Save (Object Obj) {
IF (obj == null) throw new nullreferenceException ("Attempted to Save Null);
IF (! NHIBERNATE.IINIALIZED (OBJ))
Throw New PersistentObjectException ("Uninitialized Proxy Passed To Save ()");
Object theobj = unproxyandreassociate (obj);
EntityEntry E = geTENTRY (THEOBJ);
IF (e! = null) {
IF (e.status == status.delete) {
Flush ();
}
Else {
Log.debug ("Object Already Associated with Session);
Return E.ID;
}
}
Object ID;
Try {
ID = getPersister (THEOBJ) .IDENTIFIERGENERATOR.GENERATE (this, theobj);
IF (id == (Object) IdentifierGeneratorFactory.shortcircuitindicator)
Return getIdentifier (theobj); // Todo: yick!
}
Catch (Exception EX) {
Throw New AdoException ("Could Not Save Object", EX);
}
Return DOSAVE (THEOBJ, ID);
}
First get a state of lasting objects, if you delete, flush; then obtain the ID (identifier) of a persistent object, and finally call the DOSAVE method. For the identifier of persistent object, please refer to my next article "Persistent Object Identifier".
Public void save (Object obj, object id) {
IF (obj == null) throw new nullreferenceException ("Attemted to INSERT NULL);
IF (id == null) throw new nullreferenceException ("Null Identifier Passed to INSERT ()");
IF (! NHibernate.isinitialized (OBJ)) Throw New PersistentObjectException ("Uninitialized Proxy Passed To Save ()")
Object theobj = unproxyandreassociate (obj);
EntityEntry E = geTENTRY (THEOBJ);
IF (e! = null) {
IF (e.status == status.delete) {
Flush ();
}
Else {
IF (! id.equals (e.id))
Throw New PersistentObjectException ("...");
}
}
DOSAVE (THEOBJ, ID);
}
Unlike the previous Save method, it is clear that this method is applicable to the case where the object identifier is known without obtaining a lasting object, which will improve some performance.
Private Object DOSAVE (Object Obj, Object ID) {
IclassPersister Persister = getPersister (OBJ);
Key key = NULL;
Bool Identitycol;
IF (id == NULL) {
IF (persister.isidentifierassignedbyinsert) {
Identitycol = true;
}
Else {
Throw new assertionfailure ("null id");
}
}
Else {
Identitycol = false;
}
IF (! Identitycol) {
// if the id is generated by the db, We Assign THE KEY LATER
Key = new key (id, person);
Object Old = geTENTITY (KEY);
IF (OLD! = null) {
IF (GetEntry (OLD) .status == status.deleted) {
Flush ();
}
Else {
Throw new hibernateException ("..."));
}
}
Persister.setIdentifier (OBJ, ID);
}
// ...
// Put a placeholder in Entries, ...
Addentry (Obj, Status.saving, Null, ID, Null, LockMode.Write, Identitycol, Persister);
// cascade-save to body-to-one before the parent is saved
// ...
// set Property Values ...
Identitycol {TRY {
ID = Persister.Insert (Values, Obj, this);
}
Catch (Exception E) {
Throw New AdoException ("Could Not Insert", E);
}
Key = new key (id, person);
IF (getentity (key)! = NULL)
Throw new hibernateException ("...");
Persister.setIdentifier (OBJ, ID);
}
Addentity (Key, Obj);
Addentry (Obj, Status.load, Values, ID, Versioning.getversion (Values, Persister), LockMode.write, Identitycol, Persister;
IF (! Identitycol) Insertions.add (New ScheduledInsertion (ID, Values, Obj, Persister, this);
// cascade-save to collections after the collection Owner Was Saved
// ...
Return ID;
}
The DOSAVE method first determines if the ID is assigned (ASSIGN) and then adds a persistent object to the current session collection.
If the ID is the Identity type, the persistent object's persistence class is called to insert the data, otherwise the persistent object is added to the INSERTIONS collection until the data is inserted when Flush is called.
4. UPDATEUPDATE The processing is basically similar to create, but it is worth noting that the UPDATE method does not add a persistent object to the Updates collection, but when performing FLUSH, by judging the properties of the persistent object by judging whether the persistent object is required. Update.
5. DeleteDelete is more complex, including processing collection and cascading, which is not posted here. I will analyze in subsequent articles about collection and cascading.