NHibernate代码解析 - SqlCommand - SqlString 参数名后绑定

本文介绍NHibernate中参数名后绑定的概念及其实现机制,包括SqlString、SqlStringBuilder及ISqlStringVisitor等核心组件的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    NHibernate-1.2.0.Beta3-debug

    NHibernate采用参数名后绑定方式。
    根据实体属性映射信息生成相关操作的SQL语句时,并不生成参数名,在SQL字符串层面用一个通配符?表示参数名,在对象层面用SqlCommand目录 下的Parameter类表示一个参数。这样有利于中间对SQL语句的的分析和再处理。在SQL语句快要执行之前,NH才生成具体的参数名,填入到SQL 语句之中,并把对应的参数名、参数值赋值到DbParameter对象上。
    为了支持这种处理,相关的几个最基础的类便是SqlCommand目录下的SqlString、SqlStringBuilder、ISqlStringVisitor。
  
    1. SqlString
    代表一个SQL语句字符串。它内部将一个完整的SQL语句切分成一个个独立的部分(parts)进行管理,目的就是支持参数名后绑定。例如SQL语句 SELECT * FROM TableA WHERE Col1=? AND Col2='asdf? asdf' AND Col3=?,在SqlString内部表示为下面四个部分(parts):
SELECT * FROM TableA WHERE Col1=
{?}
AND Col2='asdf? asdf' AND Col3=
{?}
    每一行表示一个part。part分为两种类型:System.String和NHibernate.SqlCommand.Parameter,上面{?}表示是一个Parameter对象。
    用下面的代码创建一个SqlString对象,调试进去可以查看到上面的结果。
    SqlString sqlstring = SqlString.Parse("SELECT * FROM TableA WHERE Col1=? AND Col2='asdf? asdf' AND Col3=?");
    在NH内部,SqlString只是一个数据格式的承载者,不能通过这个类直接去操作各个part,要创建、重新编辑SqlString对象,使用SqlStringBuilder,要遍历访问各个part,通过ISqlStringVisitor。
    创建SqlString时需要注意:a. SQL语句中不能包含注释  b. 参数以单个?表示  c. 单引里面的字符串值如果包含单引号,应当写成两个单引号才不会有问题。
    2. SqlStringBuilder
    用一个完整的SQL语句创建SqlString对象,可以直接使用SqlString.Parse(string)方法;如果基于多个parts拼装一个 SqlString对象,或者是对已有的SqlString对象重新编辑/拼装,使用SqlStringBuilder。
    SqlStringBuilder的使用者还是得了解SqlString对象的工作模式,严格的按照各个part的顺序使用相应的方法逐个添加。
    对于添加String或者Parameter类型的part,处理很简单;添加一个SqlString,在处理上复杂点,使用一个内嵌类AddingSqlStringVisitor。
    3. ISqlStringVisitor
    通过Visitor模式、SqlStringBuilder类,SqlString类本身的职责变得很单纯:以自己的数据结构表示一个SQL语句,为 ISqlStringVisitor实现者提供访问自己的方法。SqlString对象的Visit方法中按照顺序遍历各个part,把part传给 ISqlStringVisitor相关方法。
    ISqlStringVisitor的实现者AddingSqlStringVisitor将各个part添加到拥有者SqlStringBuilder 中,从而实现在SqlStringBuilder中把SqlString的各个part添加进来。而访问者SqlStringFormatter的主要目 的是将SqlString中的Parameter对象生成参数名,从而得到最终可执行的SQL语句。

    4. 参数名后绑定
    根据各种映射信息来构造SQL语句时,统一的使用一个?或者Parameter对象,有利于构造、编辑处理中对参数的识别。使用这种方式处理,就只能按照 约定的命名规则来生成参数、操作参数,从DriverBase的ToParameterName(int index)方法可以看到,就是用前缀p+参数位置索引来生成参数名。
    参数名后绑定的实现,查看DriverBase的SetCommandText方法。
using NHibernate; using NHibernate.Cfg; using System; namespace Nebula.EAP.Core.Scenario.Dao.Session { /// /// 读写分离 /// public class ReadWriteSessionManager : IDisposable { /// /// 读操作工厂 Lazy /// private static readonly Lazy _readSessionFactory = new Lazy(() => BuildSessionFactory("EAP_ORA_READ")); /// /// 写操作工厂 Lazy /// private static readonly Lazy _writeSessionFactory = new Lazy(() => BuildSessionFactory("EAP_ORA_WRITE")); private static readonly IConnectionProvider _connectionProvider; static ReadWriteSessionManager() { _connectionProvider = new ConnectionProvider(); } /// /// 读操作 /// /// public ISession OpenReadSession() => _readSessionFactory.Value.OpenSession(); /// /// 写操作 /// /// public ISession OpenWriteSession() => _writeSessionFactory.Value.OpenSession(); /// /// 读写操作 /// /// public ReadWriteSession OpenReadWriteSession() => new ReadWriteSession(OpenReadSession(), OpenWriteSession()); /// /// 创建session工厂 /// /// /// /// private static ISessionFactory BuildSessionFactory(string key) //读写工厂各执行一次 { var config = new Configuration(); config.Configure(); config.Properties["connection.connection_string"] = _connectionProvider.GetConnectionString(key); return config.BuildSessionFactory(); } public void Dispose() { } } public class ReadWriteSession : IDisposable { public ISession readSession { get; } public ISession writeSession { get; } public ReadWriteSession(ISession readSession, ISession writeSession) { this.readSession = readSession; this.writeSession = writeSession; } public void Dispose() { readSession?.Dispose(); writeSession?.Dispose(); } } } 这是我读写分离的功能 , 帮我添加一个功能 , 如果 写操作 写到 只读库 , 则报错
最新发布
06-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值