概述
一般情况下我们在设计表时主键都是自增长的,我们都是采用数据库本身提供的主键自增机制,Sqlserver一般是用IDENTITY(1,1)这个来标识,Oracle一般是用触发器,在大多数应用场景下这些机制基本上是够用的,但是在某些场景下如果需要自定义主键生成机制,例如生成L0000001、010001、000001等这些主键,利用关系数据库本身的机制就满足不了了。
这里介绍一种能够灵活定制主键的方式,它主要通过一定的生成机制来生成符合要求的主键,这里拿一个主键值初始化值来介绍:
主键初始化值 |
前缀 |
长度 |
备注 |
L0000001 |
L |
7 |
|
010001 |
01 |
4 |
|
000001 |
|
6 |
|
需求分析及设计
|
功能 |
质量 |
约束 |
组织、用户、开发 |
1、 主键灵活定制; 2、 主键生成表数据结构设计; 3、 主键生成调用接口API封装;
|
1、 安全性; 2、 通用性; 3、 性能; 4、 灵活性; |
1、 读写线程问题; 2、 无需额外配置,自动记录主键最大值; |
数据结构设计
名称 |
代码 |
注释 |
数据类型 |
长度 |
是否主键 |
表名称 |
GC017_Table_Name |
|
varchar(100) |
100 |
TRUE |
前缀编码 |
GC017_Parent_Code |
|
varchar(20) |
20 |
TRUE |
编码级别 |
GC017_Code_Level |
|
varchar(20) |
20 |
TRUE |
最大编码 |
GC017_Max_Code |
|
varchar(20) |
20 |
FALSE |
列名称 |
GC017_Column_Name |
|
varchar(20) |
20 |
TRUE |
列类型 |
GC017_Column_Type |
|
varchar(10) |
10 |
FALSE |
代码赏析
1、核心代码主要有两个:ITableCode接口和它的实现类TableCode,调用方式如下:
ITableCode tablecode = new TableCode();
// 模式1:生成主键id,参数依次为表名称、主键、主键生成长度,传入6表示000001
string id = tablecode.NewCode("tableName", "column", "6");
// 模式2:生成主键id,表示0010001
id = tablecode.NewCode("tableName", "column","2","001","4");
2、TableCode中的生成编码的核心方法:
private static readonly ReaderWriterLock l = new ReaderWriterLock();
private static readonly string prefix = string.Empty;
/// <summary>
/// 根据规则生成主键Tablecode
/// </summary>
/// <param name="pTblName">表名称</param>
/// <param name="pColumn">列名称</param>
/// <param name="pLevel">级别默认为空</param>
/// <param name="pParentCode">前缀</param>
/// <param name="pLength">长度</param>
/// <returns></returns>
public string NewCode(string pTblName, string pColumn, string pLevel, string pParentCode, string pLength)
{
try
{
l.AcquireWriterLock(Timeout.Infinite);
if (pParentCode.Equals(string.Empty))
pParentCode = "0";
if (pLevel.Equals(string.Empty))
pLevel = "1";
ArrayList objList;
objList = GetData(pTblName, pColumn, pLevel, pParentCode);
if (!pLevel.Equals("1"))
{
if (!pParentCode.Equals("0"))
{
if (objList.Equals(null))
{
string newcode = pParentCode + IncCode(string.Empty, pLength);
InsertData(pTblName, pColumn, pLevel, pParentCode, newcode);
return newcode;
}
if (objList.Count == 0)
{
string newcode = pParentCode + IncCode(string.Empty, pLength);
InsertData(pTblName, pColumn, pLevel, pParentCode, newcode);
return newcode;
}
if (objList.Count == 1)
{
string newcode = IncCode(((TableCodeInfo)objList[0]).MaxCode, pLength);
SetData(pTblName, pColumn, pLevel, pParentCode, newcode);
return newcode;
}
}
}
if (objList.Equals(null))
{
string newcode = IncCode(string.Empty, pLength);
InsertData(pTblName, pColumn, pLevel, pParentCode, newcode);
return newcode;
}
if (objList.Count == 0)
{
string newcode = IncCode(string.Empty, pLength);
InsertData(pTblName, pColumn, pLevel, pParentCode, newcode);
return newcode;
}
if (objList.Count == 1)
{
string newcode = IncCode(((TableCodeInfo)objList[0]).MaxCode, pLength);
SetData(pTblName, pColumn, pLevel, pParentCode, newcode);
return newcode;
}
}
finally
{
l.ReleaseWriterLock();
}
return string.Empty;
}
这里用到了多线程的ReaderWriterLock,方法中调用l.AcquireWriterLock(Timeout.Infinite);这样就可以保证读的时候是多线程,写的时候是单线程,避免了主键错乱的问题;
代码完整Demo下载:SystableCode.rar