NHibernate source analysis four persistence operations and SQL statements

zhaozj2021-02-16  136

The persistence operation is done by the persistence class associated with a persistent object, and the persistence class is class that implements the IClassPersister interface. Each lasting object has an associated persistence class, which stores in the ClassPersisters collection of session factories. NHibernate allows the user to persist data over a custom persistence class.

The ICLASSPERSISTER interface defines basic crud operations, implemented by AbstractTentityPersister classes in NHibernate, which is an abstract class, with two specific derived classes, respectively: EntityPersister and NormalizedentityPersister, the former is used for a table, after In a subclass of a table.

Introduce several auxiliary classes before analytical persistence:

1. SQLSTRING: Used to construct IDBCommand objects;

2. SQLStringBuilder: Used to construct the SqlString object;

3. SQLINSERTBUILDER: SQLString objects used to construct Insert operation;

4. SQLUPDATEBUILDER: SQLString object used to construct an UPDATE operation;

5. SqlDeletebuilder: SQLString object used to construct the Delete operation;

6. Parameter: Used to convert to implement IDBParameter interface objects;

7. iprepare: Used to prepare and store the IDBCommand interface, implemented by prepareImpl.

The following is an example of a persistent process as an example of an Identity annotation.

INSERT, because the persistent object has an Identity identifier, so the Save operation is executed, the persistence class of the persistent object is immediately called to perform the INSERT operation, not to join the plan collection (for why, please refer to NHibernate source code analysis.). But the final processing method is consistent.

// *** EntityPersister.cs ***

Public override void insert (Object [] Fields, Object Obj, iSession ISSIMPLEMEMENTOR session) {

Usedynamicinsert) {

Bool [] notnull = getnotnullinsertableColumns (fields);

INSERT (Fields, NotnULl, GenerateInsertstring (False, Notnull), Obj, Session

}

Else {

Insert (Fields, PropertyInsertability, Sqlinsertstring, Obj, Session);

}

}

Insert method first check if Dynamic-Insert (dynamically insert) is used, if you are inserted into a non-empty field, Dynamic-Insert can specify in the mapping file; then obtain the Insert operation SQLString by the generateInsertString method.

Protected Virtual Sqlstring GenerateInsertString (Bool IdentityInsert, Bool [] IncludeProperty) {

SqlinsertBuilder Builder = New Sqlinsertbuilder (Factory);

Builder.SettableName (TableName);

For (int i = 0; i

IF (includeproperty [i]) builder.addColumn (PropertyColumnNames [i], PropertyTypes [i]);

IF (ISPOLYMORPHIC) Builder.AddColumn (DiscriminatorColumnname, DiscriminatorSqlstring);

IF (IdentityInsert == False) {

Builder.addColumn (IdentifierColumnNames, Identifiertype);

}

Else {

IF (Diagect.IdentityInstring! = null) {

Builder.addColumn (IdentifierColumnnames [0], Diagect.IdentityInstring);

}

}

Return builder.tosqlstring ();

}

First construct a SQLInsertBuilder object, then join all fields to be inserted, the HydRateSpan defines in the AbstractTentityPersister class, is the attribute count of persistent objects; then determines whether the persistent object is PolyMorphic (more than the field value), if it is added to the column, The identification value can be specified by the discriminator-value in the mapping file; then determine whether or not to join the identity column, finally returns a SQLString object. Note: In the TOSQLString method, you can see the INSERT INTO statement we are familiar with.

The AddColumn method of the SQLInsertBuilder class has multiple overloads, which only lists more important one (join the parameter column).

// *** SQLINSERTBUILDER.CS ***

Public SQLINSERTBUILDER AddColumn (String [] ColumnNames, ITYPE PROPERTYTYPE) {

Parameter [] Parameters = Parameter.GenerateParameters (Factory, ColumnNames, PropertyType);

For (int I = 0; i

THIS.COLUMNNAMES.ADD (ColumnNames [I]);

ColumnValues.Add (Parameters [i]);

}

Return this;

}

Create parameter by parameter's static method generateParameters, then join.

// *** EntityPersister.cs ***

Public Object Insert (Object [] Fields, Bool [] notnull, sqlstring sql,

Object obj, iSessionImplementor session) {

IDBCommand Statement = NULL;

IDBCommand IDSelect = NULL;

IF (Diagect.SupportsidentitySelectinInsert) {

Statement = session.preparer.prepareCommand (Diagect.addIndentitySelectToInsert (SQL));

IDSELECT = statement;

}

Else {

Statement = session.preparer.prepareCommand (SQL);

IDSELECT = session.preparer.prepareCommand (SqlidEntitySelect);

Try {

Dehydrate (Null, Fields, PropertyInsertability, Statement, SESSION);

}

Catch (Exception E) {

Throw new hibernateException ("...", e);

}

Try {

IF (Diagect.supportsidentitySelectinInsert == false) {

Statement.executenonQuery ();

}

IDataReader RS ​​= idselect.executeReader ();

Object ID;

Try {

IF (! rs.read ()) throw new hibernateException ("...");

ID = IdentifiergeneratorFactory.get (RS, Identifiertype.ReturnedClass);

}

Finally {

Rs.close ();

}

Return ID;

}

Catch (Exception E) {

Throw e;

}

}

First declare two IDBCommand objects, Statement is used for INSERT, IDSELECT is used to check the identifier, and then prepare idbcommand objects via the iPreParer interface, such as DIALECT (database dialect) supports the INSERT statement, then retrieves INSERT and check Identifies The symbol merges; then call Dehydrate assigns the parameters; finally execute IDBCommand and return the ID.

IPreParer is used to prepare IDBCommand objects and store the IDBCommand objects that have been performed in the current session in the HashTable table, which can improve performance for bulk operations, and IDBCommand objects are created by SQLString objects.

// *** sqlstring.cs ***

Public IDBCommand BuildCommand (iDriver driver) {

INT paramindex = 0;

IDBCommand cmd = driver.createcommand ();

Stringbuilder Builder = New StringBuilder (Sqlparts.Length * 15);

For (INT i = 0; i

Object part = SQLPARTS [I];

Parameter Parameter = Part as parameter;

IF (Parameter! = null) {

String paramname = "p" paramindex;

Builder.Append (Parameter.getsqlname (Driver, paramname);

IdbdataParameter dbparam = parameter.GetidBdataParameter (CMD, Driver, paramname);

Cmd.Parameters.Add (dbparam);

PARAMINDEX ;

}

Else {

Builder.Append (String) Part);

}

}

Cmd.commandtext = builder.tostring (); return cmd;}

Create an IDBCOMMAND object via Driver, if part (part of the SQL statement) is Parameter, create IDBDataParameter and add it to IDBCommand. All Parts make up the SQL statement. // *** encityPersister ***

Protected Virtual Int Dehydrate (Object ID, Object [] Fields, Bool [] INCLUDEPROPERTY,

IDBCommand St, iSessionImplementor session) {

INT INDEX = 1;

INDEX = 0;

For (int J = 0; J

IF (IncludeProperty [J]) {

PropertyTypes [J] .nullsafeset (ST, Fields [J], INDEX, SESSION)

Index = PropertyColumnpans [J];

}

}

IF (id! = null) {

Identifiertype.Nullsafeset (ST, ID, INDEX, SESSION);

INDEX = IdentifierColumnNames.Length

}

Return Index;

}

Dehydrate traversed the properties of persistent objects and invokes the NullSafeSet method of the attribute type (ITYPE interface) assigns the parameters in the IDBCommand object, which defines in the AbstractTentityPersister class to all attribute types.

// *** nullabletype.cs ***

Public void nullsafeset (idbcommand cmd, object value, int index) {

IF (value == null) {

(IDataParameter) cmd.parameters [index]) .value = dbnull.value;

}

Else {

Set (cmd, value, index);

}

}

If null, set the parameter value to dbnull.Value; otherwise call the SET method, the SET method is the Virtual method.

(to be continued...)

转载请注明原文地址:https://www.9cbs.com/read-8561.html

New Post(0)