DbEntry 之 ORM

[ 1006 浏览 ][手机版]

赞助商
ORM 是 DbEntry 的核心,这一点在刚开始时就是方向,只是在执行 SQL 的代码已经比较稳定后才开始做。

  我不是一个迷信 Property 的人,所以,最早的 DbEntry 的 ORM 是针对 Field 的:
  1. public class User : IDbObject
  2. {
  3.     [DbKey] public long Id;
  4.     public string Name;
  5. }
复制代码
我习惯每一个表里都有一个 Id,不管其中是否有什么“唯一”的列,而且,它的名字就叫 Id,而不是什么 UserId,所以就不用在代码里写 user.UserId 这种代码,我也推荐其他人这样做,所以在 DbEntry 中,虽然也可以像上面的代码一样自己定义 Id,不过,鼓励的做法是继承自一个已经有 Id 的系统类:
  1. public class User : DbObject
  2. {
  3.     public string Name;
  4. }
复制代码
一个小插曲,以前为电信做的一个项目,客户要求不能使用 Identity,我和同事都觉得不可理解,沟通过几次,比较困难,就用一些迂回的方式解决了。后来在网上查了,才发现,早期 Sybase 的版本 Identity 的实现有 Bug,有可能造成问题,当然现在 Sybase 应该已经解决这个问题,而且,其它厂商的数据库也都没有这个问题,那么是否应该因为某个数据库的某个 Bug 而禁用 Identity 呢?我的答案在 DbEntry 里已经很清楚了。

  在 NHibernate 中,有很多种主键生成方式可供选择,Identity 啊,序列啊,NHibernate 生成啊,如果你用 Sql Server,选择 Identity 方式,那么在 Oracle 下,你需要修改它为序列方式。在我看来,不需要这么多的选择,用户真正关心的,并不是主键的生成方式,用户真正需要的就是一个自增关键列,所以,在 DbEntry 中,定义主键的方式就是上面那样,如果用户配置使用 Sql Server,就会自动使用 Identity 方式,如果用户配置使用 Oracle,就自动使用序列方式。DbEntry 也支持 GUID 作为主键,DbEntry 会自动生成它,所以,调用代码和使用 int、long 型的主键没什么区别。

  在 NHibernate/Castle ActiveRecord 里,一个字符串,如果没有任何额外定义,其长度是一个比较小的值,大概是250左右吧,如果要定义一个 TEXT 类型的列,需要定义其长度为一个很大的整数,我记不得那个整数,每一次都是复制的;在 DbEntry 里,一个没有任何额外定义的字符串,就是 TEXT 类型的,除非用户定义了长度。这个原则对于二进制型的列也相同。另外,DbEntry 中的长度定义,包括了定义最小长度项,它和最大长度一起会被用于调用数据库前的数据校验(可选),在 Hibernate(Java版,比 NH 先进点儿) 里,字符串在数据库里的长度定义,和校验的长度定义,隶属于不同的组件,所以需要把最大长度写两遍,而在 DbEntry 里,避免重复代码是一个重要的原则。
  1. public class User : DbObject
  2. {
  3.     public string Text; // NTEXT

  4.     [Length(10, 50)]
  5.     public string Fixed; // min:10, max50
  6. }
复制代码
在 Hibernate 里,一个列,如果没有标记 NotNull,则默认它是支持 Null 的,而我认为,数据库中,缺省不应该把列设置为 Null,除非你明确的想要它是可为 Null 的,所以,在 DbEntry 里,一个列,如果没有标记 AllowNull,则默认它是 Not Null 的。这里有个例外,就是对于 ValueType,使用可空类型来定义其是否可为 Null,而不需要,也不允许写 AllowNull:
  1. public class User : DbObject
  2. {
  3.     [AllowNull]
  4.     public string Null;

  5.     public string NotNull;

  6.     public int NotNullInt;

  7.     public int? NullInt;
  8. }
复制代码
和大多数其他的 ORM 比较大的不同点还有一个,就是,DbEntry 在每一次用户调用 Save 的时候,都立即生成 SQL 并提交给数据库,而不是最后来一个总的 Commit,这和我习惯的数据库访问方式相似 —— 我可以控制我希望调用 Save 时机 —— 希望这也和你的相似。另外,就像在上一次说的,源自 DbEntry 执行事务的方式,只要我们定义了 UsingTransaction,不管是 ORM 发出的数据库调用,还是直接调用 SQL 语句,只要在这个 Scope 内,都将在一个事务内,这是符合对于事务的基本预期的。

  Ruby On Rails 的 ORM 组件很有特色,Castle ActiveRecord 就是对它的一个模仿。DbEntry 里也模仿了它的一些特性,比如 SpecialName:
  1. public class User : DbObject
  2. {
  3.     [SpecialName] public DateTime CreatedOn;
  4.     [SpecialName] public DateTime? UpdatedOn;
  5. }
复制代码
这里的 CreatedOn 会在调用 Insert 时被自动赋予当前时间,而 UpdatedOn 会在调用 Update 时被自动赋予当前时间。不同于通常的 u.CreatedOn = DateTime.Now 的方式,DbEntry 总是调用数据库的时间函数来进行赋值,这样可以避免因为 Web 服务器和数据库服务器之间的时间不统一造成的统计误差。另外,SpecialName 还包含 SavedOn,Count,以及用于版本控制的 LockVersion,如果定义了被标记为 SpecialName 的 LockVersion 列,则 Save 的时候会先比较 LockVersion 的数值,如果不同,则不会更新,并引发异常,也就是通常所说的乐观锁方式。
赞助商
赞助商
TOP