package cn.com.do1.component.yopin.util;

import cn.com.do1.common.dac.QuickDAC;
import cn.com.do1.common.exception.BaseException;
import cn.com.do1.common.util.AssertUtil;
import cn.com.do1.dqdp.core.DqdpAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
*
* 自动编码工具类 <br/>调用 AutoCodeUtil.getAutoCode("传入编码前缀字符串")
* @author ao.ouyang
* 2015-4-23 10:00:17
*/
public final class AutoCodeUtil {
private final static Logger log = LoggerFactory.getLogger(AutoCodeUtil.class);
private final static ThreadLocal<Integer> executeCount = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
private static DataSource ds;
private static AutoCodeUtil util;
private final static Lock lock = new ReentrantLock(true);

private AutoCodeUtil() {
ds = DqdpAppContext.getSpringContext().getBean(DataSource.class);
}

public static AutoCodeUtil getInstance() {
if (null == util) {
lock.lock();
try {
util = new AutoCodeUtil();
} finally {
lock.unlock();
}
}
return util;
}

/**
* 这个方法不能直接调用,因为没有加锁来控制并发
* @param type
* @return
* @throws BaseException
*/
private long getCurrentVal(String type) throws BaseException {
try {
Connection conn = ds.getConnection();
QuickDAC dac = null;
try {
dac = new QuickDAC(conn);
dac.preparedSql("select BEGINNING_VAL,INCREASE_STEP,CURRENT_VAL from tb_sequence t where t.val_type = UCASE(:type)");
dac.setPreValue("type", type);
Map<String, Long> query = dac.executeQuery();
if (!AssertUtil.isEmpty(query)) {
dac.preparedSql("update tb_sequence set current_val = case when current_val + increase_step > beginning_val then current_val + increase_step else beginning_val end where val_type = UCASE(:type)");
dac.setPreValue("type", type);
dac.executeUpdate();
return Math.max(query.get("CURRENT_VAL") + query.get("INCREASE_STEP"), query.get("BEGINNING_VAL"));
} else {
dac.preparedSql("insert into tb_sequence(val_type,current_val) values(UCASE(:type), :val)");
dac.setPreValue("type", type).setPreValue("val", 1);
dac.executeUpdate();
log.info("创建新的类别流水号:{}", type);
return 1L;
}
} finally {
if (null != dac)
dac.destoryWithoutConnection();
if (null != conn)
conn.close();
}
} catch (SQLException e) {
throw new BaseException(String.format("获取%s系统流水号失败", type), e);
}
}

private long getCurrentValAndLock(String type) throws BaseException {
try {
if (lock.tryLock(30, TimeUnit.MILLISECONDS)) {
try {
executeCount.set(0);
return this.getCurrentVal(type);
} finally {
lock.unlock();
}
} else {
log.warn("在指定的时间内未能获取指定类别的流水号:{}", type);
if (5 <= executeCount.get()) {
log.error("系统连续5次获取指定类别的流水号失败:{}", type);
return -1L;
}
executeCount.set(executeCount.get() + 1);
return getCurrentValAndLock(type);
}
} catch (InterruptedException e) {
throw new BaseException(e);
}
}

/**
* 流水号开头,不包括type这个类别标识,生成的流水号:00000002321
* @param type
* @param length
* @return
* @throws BaseException
*/
private String currentSequence(String type, int length) throws BaseException {
long value = getInstance().getCurrentValAndLock(type);
StringBuilder sb = new StringBuilder("0000000000000000000");
sb.append(value); //sb=00000000000000000001234567
sb.reverse(); //sb=76543210000000000000000000
sb.setLength(length); //sb=7654321000
return sb.reverse().toString(); //sb=0001234567
}

/**
* 按照指定类别名称,获取当前流水号,返回的流水号开头显示类别名称
* @param type 流水类别名称
* @return
* @throws BaseException
*/
public String currentSequence(String type) throws BaseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHH");
return type + sdf.format(new Date()) + this.currentSequence(type, 5);
}

/**
* 按默认类别获取当前的流水号,返回的流水号开头不显示类别名称
* @return
* @throws BaseException
*/
public String currentSequence() throws BaseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHH");
return sdf.format(new Date()) + this.currentSequence("default", 8);
}
}

自定义流水号的autocode的更多相关文章

  1. JDE910笔记1--基础介绍及配置

    1.一般JDE部署后环境: DV:开发环境 PY:测试环境 PD:正式环境 根据端口号区分不同环境,可配置.同时,JDE默认使用分发服务器,不同环境连接为不同的数据库. 2.命名规范: 自定义项目.函 ...

  2. JDE910笔记1--基础介绍及配置[转]

    1.一般JDE部署后环境: DV:开发环境 PY:测试环境 PD:正式环境 根据端口号区分不同环境,可配置.同时,JDE默认使用分发服务器,不同环境连接为不同的数据库. 2.命名规范: 自定义项目.函 ...

  3. oracle 创建自定义的流水号

    ; --你确定流水号只要3位? 使用它的下一个值用: seq_abc_taskid.nextval查询当前值用:seq_abc_taskid.currval比如你现在要插入一行到abc,你可以 ,se ...

  4. Mysql - 存储过程/自定义函数

    在数据库操作中, 尤其是碰到一些复杂一些的系统, 不可避免的, 会用到函数/自定义函数, 或者存储过程. 实际项目中, 自定义函数和存储过程是越少越好, 因为这个东西多了, 也是一个非常难以维护的地方 ...

  5. WPF自定义RoutedEvent事件示例代码

    ************************* 引用网友,便于查找所用..... 创建自定义路由事件和应用分为6个步骤: (1)自定义路由事件参数对象 (2)声明并注册路由事件 (3)为路由事件添 ...

  6. 前端基于JQgrid实现自定义列头展示

    先上效果图   因为公司项目的需要,并且公司只有我这一个能写js的前端,这个自定义展示jqgrid列选项的需求依然是交由我写,辣么就分享一下我的工作成果. //初始化函数 multiSelectCol ...

  7. 自定义Base16加密

                                                                                              自定义Base16加 ...

  8. BarTender怎样同时打印自动日期和流水号?

    大多数条形码中都会含有日期和数量信息,而且大部分都是两者兼具.有些使用BarTender软件的小伙伴,不知道怎么同时打印自动日期和流水号,即条形码中兼有自动日期和序列号,且它们都能根据打印的变化而变化 ...

  9. C# 流水号生成器开发

    前言 本文将使用一个Nuget公开的组件技术来实现一个流水号生成器,提供了一些简单的API,来方便的实现一个通用的流水号. 在visual studio 中的NuGet管理器中可以下载安装,也可以直接 ...

随机推荐

  1. Orchard运用 - 如何删除部分内容显示

    最近在潜心研究Orchard CMS,个人觉得要深入了解一个系统最好的办法, 就是使用它做些什么尝试,最好有些真实的实践,比如不妨基于Orchard搭建个人博客, 看看有哪些场景需要定制,哪些功能可以 ...

  2. 如何对exec sp_who2的结果进行选择和排序?

    从网上找到了下面的两个脚本, 笔者试用过, 很好. 故记在这里.   方法一, 用临时表 CREATE TABLE #sp_who2 ( SPID INT, Status VARCHAR(255), ...

  3. perforce 使用教程(zz)

    http://www.perforce.com/documentation/perforce_technical_documentation http://blog.csdn.net/brucexu1 ...

  4. [Python爬虫] 之四:Selenium 抓取微博数据

    抓取代码: # coding=utf-8import osimport refrom selenium import webdriverimport selenium.webdriver.suppor ...

  5. 再谈Cognos利用FM模型来做同比环比

    很早之前已经讲过 <Cognos利用DMR模型开发同比环比>这篇文章里说的是不利用过滤器,而是采用 except (lastPeriods (-9000,[订单数据分析].[日期维度].[ ...

  6. MESI-CPU缓存一致性协议

    转http://blog.csdn.net/realxie/article/details/7317630 http://en.wikipedia.org/wiki/MESI_protocol MES ...

  7. Cocos2d-x 处理双击事件的两种方法

    在cocos2d-x的开发过程中有些时候也是需要用到双击的事件处理,那么由于在cocos2d-x中没有实现对双击的事件的处理,那么我们就需要自己用代码实现. 下面介绍两种方式实现双击事件的处理. (一 ...

  8. windows程序崩溃生成dump文件

    第一种: 通过任务管理器:这种适用在程序挂了(crash)的时候进程还未退出,比如我运行程序,出现了下面的错: 此时打开任务管理器,右击相应进程,点击"Create Dump File“: ...

  9. WCF学习笔记之序列化

    DataContractAttribute 与 DataMenberAttribute DataContractAttribute该特性只能用于枚举.类和结构体,而不能用于接口:又因为DataCont ...

  10. taro 环境判断

    Taro.ENV_TYPE ENV_TYPE.WEAPP 微信小程序环境 ENV_TYPE.SWAN 百度小程序环境 ENV_TYPE.ALIPAY 支付宝小程序环境 ENV_TYPE.TT 字节跳动 ...