自动增长的主键字段是个好东西,提供了一个比较有意义和可阅读的字段内容(相对GUID来说),插入的时候,不用管它的值,数据库自动追加;但它也是一个不好的东西,如果管理不好,可能会造成冲突。本文暂且不讨论其优劣,因为存在就是硬道理,很多时候,我们都是采用自增长字段的,特别是对于SqlServer数据开发来说。
本文阐述一下在Database2Sharp(软件下载地址:http://www.iqidi.com/database2sharp.htm)生成的Enterprise Library架构如何实现Oracle的自增长的支持。同时也会顺带说说对SqlServer、Access的实现。
Database2Sharp生成的Enterprise Library架构其实对Oracle内置了对自增长序列的支持,在数据库访问层的基类BaseDAL中,我们看到下面的代码。
/// <summary>
/// </summary>
public abstract class BaseDAL<T> : IBaseDAL<T> where T : BaseEntity, new()
{
#region 构造函数
protected string tableName;//需要初始化的对象表名
protected string primaryKey;//数据库的主键字段名
protected string sortField = "ID";//排序字段
private bool isDescending = false;//
protected string selectedFields = " * ";//选择的字段,默认为所有(*)
private string seqField = "";//指定那个字段是用序列来控制它的值的,一般为主键
private string seqName = "";//指定的序列名称,建议规则为:SEQ_表名称
/// <summary>
/// 指定那个字段是用序列来控制它的值的,一般为主键
/// </summary>
public string SeqField
{
get { return seqField; }
set { seqField = value; }
}
/// <summary>
/// 指定的序列名称,建议规则为:SEQ_表名称
/// </summary>
public string SeqName
{
get { return seqName; }
set { seqName = value; }
}
这段代码定义了两个属性,一个是序列字段名称(一般是主键,如ID),一个是我们为该字段指定的序列对象名称,我们这里建议的名称是"SEQ_表名称",当然也可以使用任意的名称,合理统一就可以了。这两个属性在基类不需要修改,只需要在具体的数据访问对象(如数据访问层中的Customer类)构造函数中,指定序列字段和序列对象即可。
下面我们看看表盒序列的脚本代码,例如我创建一个客户表,其字段ID为自增序列,我的创建脚本是。
create table ALL_CUSTOMER
ID NUMBER not null,
USERNUMBER VARCHAR2(50),
NAME VARCHAR2(50),
TYPE VARCHAR2(50),
AREA VARCHAR2(50),
COMPANY VARCHAR2(50),
ADDRESS VARCHAR2(50),
TELEPHONE1 VARCHAR2(50),
TELEPHONE2 VARCHAR2(50),
TELEPHONE3 VARCHAR2(50),
TELEPHONE4 VARCHAR2(50),
TELEPHONE5 VARCHAR2(50),
CREATEDATE DATE,
SHOP_ID VARCHAR2(50),
NOTE VARCHAR2(255),
LASTUPDATED DATE,
constraint PK_ALL_CUSTOMER primary key (ID)
);
create sequence SEQ_ALL_CUSTOMER
minvalue 1
maxvalue 999999999999999999999999999
start with 1220
increment by 1
cache 20;
commit;
注意SEQ_ALL_CUSTOMER就是序列对象名称,那么我们再插入的时候,应该如何写入序列字段的值,并且获得新的值作为返回值的呢?
在BaseDAL中,有一个Insert2的方法,是专门处理 自增序列函数,并且返回创建记录的自增字段的值的,我们来看看其源码。
/// <summary>
/// </summary>
/// <param name="recordField">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <param name="targetTable">需要操作的目标表名称</param>
/// <param name="trans">事务对象,如果使用事务,传入事务对象,否则为Null不使用事务</param>
public int Insert2(Hashtable recordField, string targetTable, DbTransaction trans)
{
int result = -1;
string fields = ""; // 字段名
string vals = ""; // 字段值
if (recordField == null || recordField.Count < 1)
{
return result;
}
List<OracleParameter> paramList = new List<OracleParameter>();
IEnumerator eKeys = recordField.Keys.GetEnumerator();
while (eKeys.MoveNext())
{
string field = eKeys.Current.ToString();
fields += field + ",";
if (!string.IsNullOrEmpty(seqField) && !string.IsNullOrEmpty(seqName)
&& (field == seqField))
{
vals += string.Format("{0}.NextVal,", seqName);
}
else
{
vals += string.Format(":{0},", field);
object val = recordField[eKeys.Current.ToString()];
paramList.Add(new OracleParameter(":" + field, val));
}
}
fields = fields.Trim(',');//除去前后的逗号
vals = vals.Trim(',');//除去前后的逗号
string sql = string.Format("INSERT INTO {0} ({1}) VALUES ({2})", targetTable, fields, vals);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(sql);
command.Parameters.AddRange(paramList.ToArray());
if (trans != null)
{
db.ExecuteNonQuery(command, trans);
sql = string.Format("SELECT {0}.Currval ID From Dual", seqName);
command = db.GetSqlStringCommand(sql);
result = Convert.ToInt32(db.ExecuteScalar(command, trans).ToString());
}
else
{
db.ExecuteNonQuery(command);
sql = string.Format("SELECT {0}.Currval ID From Dual", seqName);
command = db.GetSqlStringCommand(sql);
result = Convert.ToInt32(db.ExecuteScalar(command).ToString());
}
return result;
}
其中我们判断是否有自增序列ID和其名称(非空字符),如果有则使用这段代码,来写入自增序列的下一个值NextVal(新增值),作为这个字段的值。
vals += string.Format("{0}.NextVal,", seqName);
如果要返回插入的自增序列值,那么我们使用序列对象的Currval 就可以了。下面是返回插入的字段内容。
sql = string.Format("SELECT {0}.Currval ID From Dual", seqName);
这样对于写入新的自增长值并返回就实现了。
对SqlServer和Access自增长字段的支持
对于SqlServer,实现自增长字段就更加方便了,由于没有Oracle序列对象那么麻烦,所以只需要在具体的数据库访问对象中,构建写入字段的Hash表中,忽略该字段就可以了(代码已经自动生成,不用管理的)。其返回刚刚插入的自增内容,则在插入的语句后面增加一条语句就可以了,语句如下。
SELECT SCOPE_IDENTITY()
对于Access的数据库,原理和SqlServer一样,不过需要返回刚刚插入的自增长值的时候,使用这段语句就可以了。
SELECT @@IDENTITY
对于SqlServer和Access,只要设计好数据库的自增字段,自动生成的代码中,数据访问类是不用修改任何信息,就可以完美支持自增序列。