分享
 
 
 

nhibernate源码分析之四 持久化操作与SQL语句

王朝mssql·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

持久化操作由与持久对象关联的持久化类来完成,持久化类是实现IClassPersister接口的类,每个持久对象都有一个关联的持久化类,这些持久化类存储在会话工厂的classPersisters集合中,nhibernate允许用户通过自定义的持久化类来持久化数据。

IClassPersister接口定义了基本的CRUD操作,在nhibernate中由AbstractEntityPersister类实现,这是一个抽象类,有两个具体的派生类,分别是:EntityPersister和normalizedEntityPersister,前者用于一个表一个类的情况,后面用于一个表一个子类的情况。

在分析持久化操作之前先来介绍几个辅助类:

1. SqlString: 用于构造IDbCommand对象;

2. SqlStringBuilder: 用于构造SqlString对象;

3. SqlInsertBuilder: 用于构造Insert操作的SqlString对象;

4. SqlUpdateBuilder: 用于构造Update操作的SqlString对象;

5. SqlDeleteBuilder: 用于构造Delete操作的SqlString对象;

6. Parameter: 用于转换到实现IDbParameter接口对象;

7. IPrepare: 用于准备和存储IDbCommand接口,由PrepareImpl实现。

下面以一个有identity符识的对象为例说明其持久化的流程。

一. Insert

因为持久对象有identity标识符,所以执行Save操作时,是立即调用持久对象的持久化类来执行Insert操作,而不是加入到计划集合中(原因请参考 nhibernate源码分析之三)。但最终的处理方式是一致的。

//*** EntityPersister.cs ***

public override void Insert(object[] fields, object obj, ISessionImplementor session) {

if(UseDynamicInsert) {

bool[] notNull = GetNotNullInsertableColumns(fields);

Insert(fields, notNull, GenerateInsertString(false, notNull), obj, session);

}

else {

Insert(fields, PropertyInsertability, SqlInsertString, obj, session);

}

}

Insert方法首先检查是否使用dynamic-insert(动态插入),如使用则只插入非空的字段,dynamic-insert可在映射文件中指定;然后通过GenerateInsertString方法取得insert操作的SqlString。

protected virtual SqlString GenerateInsertString(bool identityInsert, bool[] includeProperty) {

SqlInsertBuilder builder = new SqlInsertBuilder(factory);

builder.SetTableName(TableName);

for(int i = 0 ; i < hydrateSpan; 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(dialect.IdentityInsertString!=null) {

builder.AddColumn(IdentifierColumnNames[0], dialect.IdentityInsertString);

}

}

return builder.ToSqlString();

}

首先构造一个SqlInsertBuilder对象,然后加入所有要插入的字段,hydrateSpan在AbstractEntityPersister类中定义,是持久对象的属性Count;然后判断持久对象是否为Polymorphic(通过字段值实现多态),如是则加入辨别列,辨别值可通过映射文件中的discriminator-value来指定;接着判断是否要加入标识列,最后返回一个SqlString对象。注意:在ToSqlString方法中,可以看到我们熟悉的INSERT INTO语句。

SqlInsertBuilder类的AddColumn方法有多个重载,这里只列出比较重要的一个(加入参数列)。

//*** SqlInsertBuilder.cs ***

public SqlInsertBuilder AddColumn(string[] columnNames, IType propertyType) {

Parameter[] parameters = Parameter.GenerateParameters(factory, columnNames, propertyType);

for(int i = 0; i < columnNames.Length; i++) {

this.columnNames.Add(columnNames[i]);

columnValues.Add(parameters[i]);

}

return this;

}

通过Parameter的静态方法GenerateParameters创建Parameter,然后加入到集合中。

//*** EntityPersister.cs ***

public object Insert(object[] fields, bool[] notNull, SqlString sql,

object obj, ISessionImplementor session) {

IDbCommand statement = null;

IDbCommand idSelect = null;

if(dialect.SupportsIdentitySelectInInsert) {

statement = session.Preparer.PrepareCommand( dialect.AddIdentitySelectToInsert(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(dialect.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;

}

}

首先声明两个IDbCommand对象,statement用于insert,idSelect用于检查标识符,然后通过IPreparer接口来准备IDbCommand对象, 如dialect(数据库方言)支持在Insert语句中后检索标识符,则将insert与检查标识符操作合并;然后调用Dehydrate对参数进行赋值;最后执行IDbCommand并返回id。

IPreparer用于准备IDbCommand对象,并将当前会话中已执行过的IDbCommand对象存储在hashtable表中,这样对于批量操作时可以提高性能,而IDbCommand对象则由SqlString对象创建。

//*** 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 < sqlParts.Length; 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;

}

通过Driver创建IDbCommand对象,如果part(组成Sql语句的一部分)为Parameter,则创建IDbDataParameter并加入到IDbCommand中。所有的part就组成Sql语句。

//*** 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 += propertyColumnSpans[j];

}

}

if ( id!=null ) {

IdentifierType.NullSafeSet( st, id, index, session );

index += IdentifierColumnNames.Length;

}

return index;

}

Dehydrate遍历持久对象的属性,并调用属性类型(IType接口)的NullSafeSet方法对IDbCommand对象中的参数进行赋值,PropertyTypes在AbstractEntityPersister类中定义,为所有属性类型的集合。

//*** 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);

}

}

如为null,则设置参数值为DBNull.Value;否则调用Set方法,Set方法为virtual方法。

(待续...)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有