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. Nginx安装及配置文件解释

    安装nginx,还是在mac上面用brew比较方便. 首先,brew install nginx,提示改权限 sudo chown -R $(whoami) /usr/local 然后brew ins ...

  2. JavaScript 你不知道的事 -- 关于函数

    接上篇Javascript 你不知道的事,直接条列了: 每个函数创建时默认带有一个prototype属性,其中包含一个constructor属性,和一个指向Object对象的隐藏属性__proto__ ...

  3. Android -- Sqlite事务

    这也是通过Android Juint来实现的. 基于上一次的工程继续,上一次工程传送门:<Android–Android Juint 与 Sqlite> http://www.cnblog ...

  4. [Functional Programming] Async IO Functor

    We will see a peculiar example of a pure function. This function contained a side-effect, but we dub ...

  5. 黑苹果安装 this is an unknown cpu model 0x3a

    一:安装说明 安装的是 mac os x 10.8.3 懒人版: ASUS b75ma 主板 i3 cpu 二:安装出现错误 百度谷歌,各种找原因,各路大神们都说是显卡的问题,因这电源管理只是一个警告 ...

  6. mybatis模糊查询语句

    articletitle like concat('%',#{articletitle},'%')

  7. 安卓手机使用Fiddler抓获HTTPS报文方法

    Configure Fiddler Click Tools > Fiddler Options > Connections. Ensure that the checkbox by All ...

  8. Dao泛型设计和反射反型

    (1)DAO泛型设计:当二哥或多个类中有类似的方法时,可以将这些累死的方法提出到类中,形式一个泛型父类 (2)反射反型:在泛型父类中获取子类的具体类型的过程,叫反射反型 package cn.itca ...

  9. 【LeetCode】99. Recover Binary Search Tree

    Recover Binary Search Tree Two elements of a binary search tree (BST) are swapped by mistake. Recove ...

  10. T-sql 根据日期时间 按年份、月份、天来统计

    看统计结果: 这里利用的是convert函数,这里不得不说一下convert函数 CONVERT() 函数是把日期转换为新数据类型的通用函数. CONVERT() 函数可以用不同的格式显示日期/时间数 ...