原理类似hibernate的主键生成机制,在系统启动时从数据库中读出最大的流水号,赋给一个类的静态变量,需要时,从该静态变量中取得流水号,加1后,就是你要的值。
代码:
public class testA {
public static Long maxNo
static {
synchronized(maxNo){
maxNo = select max(maxNo) from DB
}
}
public synchronized static long getMaxNo(){
return(++maxNo)
}
}
优点:保证同步,不会取到相同的值,同时避免了数据库的反复读取。
缺点:在cluster环境下
还是用上面的方法,但是只能在一台机器上发布并且绑定到JNDI,别的机器可以通过配置文件得知去哪里产生maxNo(实际上,每台机器都可以发布,但是所有机器只能去访问指定的一台机器),这样的效率虽然低了点,但是还是觉得比每次去数据库里取好点。当然,可以改进,就是再加一个本地类,每次去JNDI那里的类去取数据时,一次取20个回来(就是JNDI上面的类一次要加20),以后本地就用这20个值,用完了,再去JNDI上面的类去取。
当然,前面的这两个例子都比较复杂,甚至还有人提出用单态的方法。而我在.net中用的就比较简单了。方法如下:
在数据库(sqlserver)中新建一张表(sequence_num),专门用来生成流水号。
字段1:s_id,varchar(50),notnull
字段2:tmstmp,timestamp,notnull
代码如下:
public string getOderid()
{
private SqlConnection conn = null
private SqlCommand comm = null
private string s_id = ""
private byte [] tmstmp//时间戳类型在.net中对应为一个byte数组
private int s_idplus = 0
private string orderid = ""
try
{
conn = SqlConn.getConn()
conn.Open()
do
{
string sqlQuery = "select s_id,tmstmp as tmstmp from sequence_num"
comm = conn.CreateCommand()
comm.CommandText = sqlQuery
SqlDataAdapter da = new SqlDataAdapter(comm)
DataTable dt = new DataTable()
dt.Clear()
da.Fill(dt)
comm.Parameters.Clear()
if (!dt.Rows[0]["s_id"].ToString().Substring(0,12).Equals(DateTime.Now.ToString("yyyyMMddHHmm")))//每一分钟重置一次计数,实际上我认为每天重置一次比较好
{
s_id = DateTime.Now.ToString("yyyyMMddHHmm")+"0001"
tmstmp = (byte[])dt.Rows[0]["tmstmp"]
}
else
{
s_id = dt.Rows[0]["s_id"].ToString()
tmstmp = (byte[])dt.Rows[0]["tmstmp"]
s_idplus = Convert.ToInt32(s_id.Substring(12)) + 1
s_id = s_id.Substring(0,12)+Convert.ToString(s_idplus).PadLeft(4,'0')
}
string sqlUpdate = "update sequence_num set s_id = @s_id where tmstmp = @tmstmp"//保证在记录未被修改的情况下更新,如果更新不成功,则重走一遍生成序列号的流程
comm.CommandText = sqlUpdate
comm.Parameters.Clear()
comm.Parameters.Add("@s_id",SqlDbType.VarChar,50)
comm.Parameters["@s_id"].Value = s_id
comm.Parameters.Add("@tmstmp",SqlDbType.Timestamp)
comm.Parameters["@tmstmp"].Value = tmstmp
}
while(comm.ExecuteNonQuery()<=0)
}
catch(SqlException ex)
{
string aaa = ex.Message
}
finally
{
if(conn!=null)
{
conn.Close()
}
}
return s_id
}
这个流程都是在.net里实现的,实际使用中可以通过存储过程来实现。当然,这种方法对数据库的访问会比较频繁,另外,数据库表建立的时候必须插入一条初始值,这是代码不完善的地方,有时间的话我会去完善它。只是判断如果表内没有记录的情况下就插入一条初始记录,这个时候如何处理并发问题还没想明白。
流水号在编辑软件中可以用序列生成或者数据库导入的方式来实现流水号的递增或者递减,在软件中设计好之后,预览没有问题的情况下,可以输出PDF文档、TIFF图片、PPML文件等,之后拿到喷码机上进行打印,设置流水号的步骤可以参考:
首先,点击左侧工具栏中“A”在画布上绘制普通文本,然后双击普通文本,打开“图形属性-数据源”下方点击左侧“修改”,选择序列生成的添加方式,然后点击“编辑-确定”即可。
使用序列生成可以得到以下效果:
从上图可以看到,数据是递增的,但是位数也是随之变化的,那么如果需要每个流水号的位数都一致的,则可以使用右侧处理方法中的补齐来实现,也就是说想要制作几位的流水号,就在下方“长度”后面输入对应位数即可:
然后点击“编辑-确定”,就可以在画布上看到对应设置的流水号数据的第一个了,这时可点击打印预览查看所有批量生成的流水号的效果:
如果想要在流水号前面增加固定前缀,可以在其数据源中点击左侧“+”,用手动输入的方式添加内容,然后“编辑-确定”,一般后来添加的内容会是在流水号的下面,这时可以选中添加的前缀,然后点击左侧向上的箭头向上移动即可。
设置完之后点击打印预览就可以看到想要的效果了:
帮你写个/// <summary>
/// 根据时间生成流水号
/// 流水号组成如XS200811050001
/// XS:销售
/// 20081105:日期
/// 0001:20081105日的第一个订单
/// </summary>
/// <returns></returns>
private string GetNumberString()
{
string orderNumber = null
//取得当天的最大订单号
DateTime now = DateTime.Now
string sql = "select max(convert(int,substring(OrderNumber,11,4))) from Orders where OrderNumber like 'XS" + now.Year.ToString("d4") + now.Month.ToString("d2") + now.Day.ToString("d2") + "%'"
DataSet ds = null
using (DatabaseOperater2 op = new DatabaseOperater2())
{
ds = op.ExcuteSelectByAdater(sql)
}
if (ds.Tables[0].Rows[0][0] is DBNull)
{
//如果今天还没有订单
orderNumber = "XS" + now.Year.ToString("d4") + now.Month.ToString("d2") + now.Day.ToString("d2") + "0001"
}
else
{
//如果有订单,则在最大订单号上+1
int number = Convert.ToInt32(ds.Tables[0].Rows[0][0])+1
orderNumber = "XS" + now.Year.ToString("d4") + now.Month.ToString("d2") + now.Day.ToString("d2") + number.ToString("d4")
}
return orderNumber
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)