做新系统开始,业务要求实现编号每天重置为1,例如:今天是2020年6月15日,贷款合同的编号需要为2020DKHT00001,2020DKHT00002。。依次递增,第二天再重新从1开始,即2020061600001,2020061600002。。
同事写好了,我直接给搬运过来,供大家做个参考。
数据库表结构如下图所示
//序列实体
public class SequenceEntity {
//序列名称
private String seqName;
//当前值
private long seqValue;
//最小值
private long minValue;
//最大值
private long maxValue;
//一次缓存序列数量
private long step;
//创建时间
private Date createDate;
//更新时间
private Date updateDate;
public String getSeqName() {
return seqName;
}
public void setSeqName(String seqName) {
this.seqName = seqName;
}
public long getSeqValue() {
return seqValue;
}
public void setSeqValue(long seqValue) {
this.seqValue = seqValue;
}
public long getMinValue() {
return minValue;
}
public void setMinValue(long minValue) {
this.minValue = minValue;
}
public long getMaxValue() {
return maxValue;
}
public void setMaxValue(long maxValue) {
this.maxValue = maxValue;
}
public long getStep() {
return step;
}
public void setStep(long step) {
this.step = step;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
public boolean validate(){
//一些简单的校验。如当前值必须在最大最小值之间。step值不能大于max与min的差
if (StringUtil.isBlankNew(seqName) || minValue < 0 || maxValue <= 0 || step <= 0 || minValue >= maxValue || maxValue - minValue <= step ||seqValue < minValue || seqValue > maxValue ) {
return false;
}
return true;
}
}
//DAO层
public interface MysqlSequenceDao {
SequenceEntity getSequenceEntity(String seqName);
int updateNewSequenceValue(String seqName, long parseLong, long newSequence);
int insertSequenceEntity(SequenceEntity se);
}
//DAO实现,这边@DalDao是自己写的注解,换成你们常用开发注解即可
@DalDao
public class MysqlSequenceDaoImpl extends DalBaseDaoMyBatis<SequenceEntity> implements MysqlSequenceDao {
@Override
public int insertSequenceEntity(SequenceEntity se){
int result = this.getSqlSession().insert(this.getEntityClass().getName()+".insertSequenceEntity",se);
return result;
}
@Override
public SequenceEntity getSequenceEntity(String seqName) {
//查询单个序列对象
SequenceEntity se = (SequenceEntity)this.getSqlSession().selectOne(this.getEntityClass().getName()+".selectSeqEntity",seqName);
return se;
}
@Override
public int updateNewSequenceValue(String seqName, long initSequence, long newSequence) {
Map map = new HashMap<String,Object>();
//where条件加上序列名称
map.put("seqName", seqName);
map.put("oldValue",initSequence);
map.put("newValue",newSequence);
map.put("updateDate",new Date());
int count = this.getSqlSession().update(this.getEntityClass().getName()+".updateSeqValue",map);
return count;
}
}
//对应xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.suning.cms.entity.SequenceEntity">
<resultMap id="sequenceEntity" type="com.suning.cms.entity.SequenceEntity">
<result column="seq_name" property="seqName" />
<result column="seq_value" property="seqValue" />
<result column="seq_min_value" property="minValue" />
<result column="seq_max_value" property="maxValue" />
<result column="seq_step" property="step" />
<result column="createdate" property="createDate" />
<result column="updatedate" property="updateDate" />
</resultMap>
<sql id="sequenceEntityColumn">
<trim suffixOverrides=",">
<if test="seqName!=null and seqName!=''">
seq_name,
</if>
<if test="seqValue!=null and seqValue!=''">
seq_value,
</if>
<if test="minValue!=null and minValue!=''">
seq_min_value,
</if>
<if test="maxValue!=null and maxValue!=''">
seq_max_value,
</if>
<if test="step!=null and step!=''">
seq_step,
</if>
<if test="createDate!=null and createDate!=''">
createdate,
</if>
<if test="updateDate!=null and updateDate!=''">
updatedate,
</if>
</trim>
</sql>
<sql id="sequenceEntityValue">
<trim suffixOverrides=",">
<if test="seqName!=null and seqName!=''">
#{seqName},
</if>
<if test="seqValue!=null and seqValue!=''">
#{seqValue},
</if>
<if test="minValue!=null and minValue!=''">
#{minValue},
</if>
<if test="maxValue!=null and maxValue!=''">
#{maxValue},
</if>
<if test="step!=null and step!=''">
#{step},
</if>
<if test="createDate!=null and createDate!=''">
#{createDate},
</if>
<if test="updateDate!=null and updateDate!=''">
#{updateDate},
</if>
</trim>
</sql>
<insert id="insertSequenceEntity" parameterType="com.suning.cms.entity.SequenceEntity">
insert into cm_biz_sequence
(
<include refid="sequenceEntityColumn"/>
)
value
(
<include refid="sequenceEntityValue"/>
)
</insert>
<select id="selectSeqEntity" parameterType="java.lang.String" resultMap="sequenceEntity">
select s.* from cm_biz_sequence s
where s.seq_name = #{seqName}
</select>
<update id="updateSeqValue" parameterType="Map">
update cm_biz_sequence b set seq_value = #{newValue}, updatedate = #{updateDate}
where seq_value = #{oldValue} and seq_name = #{seqName}
</update>
</mapper>
//用来获取缓存序列,每次缓存5个数
public class SequenceRange {
private final AtomicLong value;
private final long minCacValue;
private final long maxCacValue;
//是否超限
private volatile boolean over =false;
public SequenceRange(long oldSeqValue,long newSeqValue){
this.minCacValue=oldSeqValue;
this.maxCacValue=newSeqValue;
this.value=new AtomicLong(oldSeqValue);
}
public long getNextSequence(){
long currValue = value.getAndIncrement();
if(currValue>maxCacValue){
over=true;
return -1;
}
return currValue;
}
}
//缓存序列控制类
public class MysqlSequenceHolder {
private static final Logger LOG = LoggerFactory.getLogger(MysqlSequenceHolder.class);
private final Lock lock = new ReentrantLock();
@Resource
private MysqlSequenceDao mysqlSequenceDao;
private SequenceEntity sequenceEntity;
private SequenceRange sequenceRange;
/** 是否初始化 */
private volatile boolean isInitialize = false;
MysqlSequenceHolder(MysqlSequenceDao msDao, SequenceEntity se){
this.mysqlSequenceDao=msDao;
this.sequenceEntity=se;
}
long getNextVal(){
if(!isInitialize){
LOG.info("序列名" + sequenceEntity.getSeqName() + "未初始化");
throw new BusinessException("序列名" + sequenceEntity.getSeqName() + "未初始化");
}
long nextSeq = sequenceRange.getNextSequence();
if(nextSeq==-1){
try{
lock.lock();
nextSeq = sequenceRange.getNextSequence();
if(nextSeq!=-1){
return nextSeq;
}
sequenceRange = retryRange();
nextSeq = sequenceRange.getNextSequence();
}finally {
lock.unlock();
}
}
return nextSeq;
}
//缓存序列已用完,重新取数据库更新
private SequenceRange retryRange() {
SequenceEntity sequeEntity = mysqlSequenceDao.getSequenceEntity(sequenceEntity.getSeqName());
if(sequeEntity==null){
LOG.info("序列名" + sequenceEntity.getSeqName() + "为空");
throw new BusinessException("序列名" + sequenceEntity.getSeqName() + "为空");
}
if(!sequeEntity.validate()){
LOG.info("序列名" + sequenceEntity.getSeqName() + "参数异常");
throw new BusinessException("序列名" + sequenceEntity.getSeqName() + "参数异常");
}
//原来值
long currentSequence = sequeEntity.getSeqValue();
//更改后值
long newSequence = currentSequence+sequeEntity.getStep();
//校验当前值
if(!checkCurrentValue(newSequence,sequeEntity)){
LOG.info("序列名" + sequenceEntity.getSeqName() + "超出最大值!");
throw new BusinessException("序列名" + sequenceEntity.getSeqName() + "超出最大值!");
}
int result = mysqlSequenceDao.updateNewSequenceValue(sequeEntity.getSeqName(),sequeEntity.getSeqValue(),newSequence);
if(result>0){
sequenceRange = new SequenceRange(currentSequence,newSequence-1);
sequeEntity.setSeqValue(newSequence);
this.sequenceEntity =sequeEntity;
return sequenceRange;
}else{
LOG.info("序列名" + sequenceEntity.getSeqName() + "更新异常");
throw new BusinessException("序列名" + sequenceEntity.getSeqName() + "更新异常");
}
}
void initSequence(){
if(isInitialize){
LOG.info("序列已初始化");
return;
}
// 初始化该sequence
try {
initSequenceRecord(sequenceEntity);
} catch (Exception e) {
LOG.info("序列初始化异常",e);
throw new BusinessException("序列名" + sequenceEntity.getSeqName() + "初始化异常");
}
isInitialize = true;
}
private void initSequenceRecord(SequenceEntity sequenceEntity) {
SequenceEntity seqE = mysqlSequenceDao.getSequenceEntity(sequenceEntity.getSeqName());
//当前序列值
long currVal = seqE.getSeqValue();
//改变当前值
long newSeqVal= currVal+seqE.getStep();
if(!checkCurrentValue(newSeqVal,seqE)){
throw new BusinessException("序列名" + sequenceEntity.getSeqName() + "超出最大值!");
}
//更新数据库记录当前值为最新值
int result = mysqlSequenceDao.updateNewSequenceValue(sequenceEntity.getSeqName(),seqE.getSeqValue(),newSeqVal);
if(result>0){
sequenceRange = new SequenceRange(sequenceEntity.getSeqValue(),newSeqVal-1);
//更新记录后,把当前entity序列值更新为最新
sequenceEntity.setSeqValue(newSeqVal);
this.sequenceEntity = seqE;
}else{
LOG.info("序列名" + sequenceEntity.getSeqName() + "更新异常");
throw new BusinessException("序列名" + sequenceEntity.getSeqName() + "更新异常");
}
}
private boolean checkCurrentValue(long newSeqVal, SequenceEntity sequenceEntity) {
//判断新序列在最大值和最小值之间
return newSeqVal >= sequenceEntity.getMinValue() && sequenceEntity.getMaxValue() > newSeqVal;
}
}
//序列工厂类
@Service("mysqlSequenceFactory")
public class MysqlSequenceFactory {
private static final Logger LOG = LoggerFactory.getLogger(MysqlSequenceFactory.class);
//同步锁
private final Lock lock = new ReentrantLock();
private Map<String, MysqlSequenceHolder> holderMap = new ConcurrentHashMap<>();
@Resource
private MysqlSequenceDao mysqlSequenceDao;
//初始化序列值
private final long initSequence = 1;
//初始化序列最小值
private final long minSequence = 1;
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public long getNextVal(String seqName) {
MysqlSequenceHolder msHolder = holderMap.get(seqName);
if (msHolder == null) {
try {
lock.lock();
msHolder = holderMap.get(seqName);
if (msHolder != null) {
return msHolder.getNextVal();
}
//先要根据序列名查询数据库有么有插入或者提前配置(防止正常情况下服务停掉重启)
SequenceEntity se = mysqlSequenceDao.getSequenceEntity(seqName);
//数据库可以根据业务需求进行配置,没有配置的话,按照上边的默认值
if (se == null) {
//再次确定没有缓存,那么就插入一条记录
//初始化序列最大值
String maxSequence = SCMUtil.getScmPropValidResult(ScmConstants.MAXSEQUENCE);
//初始化缓存数STEP
String sequenceStep = SCMUtil.getScmPropValidResult(ScmConstants.SEQUENCESTEP);
if(StringUtil.isBlankNew(maxSequence)){
maxSequence = "10000";
}
if(StringUtil.isBlankNew(sequenceStep)){
sequenceStep = "5";
}
se = new SequenceEntity();
se.setSeqName(seqName);
se.setSeqValue(initSequence);
se.setStep(Long.valueOf(sequenceStep));
se.setMinValue(minSequence);
se.setMaxValue(Long.valueOf(maxSequence));
se.setCreateDate(new Date());
int result = mysqlSequenceDao.insertSequenceEntity(se);
LOG.info("序列bean插入成功:" + result);
}
msHolder = new MysqlSequenceHolder(mysqlSequenceDao, se);
msHolder.initSequence();
holderMap.put(se.getSeqName(), msHolder);
}catch (Exception e){
LOG.info("获取序列异常",e);
throw new BusinessException("获取序列异常");
} finally {
//不管插入成功与否都要释放锁
lock.unlock();
}
}
return msHolder.getNextVal();
}
}
//序列service接口
public interface MysqlSequenceService {
String getNextSequnce(String seqName);
String getNextLogId();
String getNextCode(String type);
String getNextPayReqNo(String bizSubCateg,String distributeSys);
}
//序列Service实现类,重点关注第一个方法即可
@Service("mysqlSequenceServiceImpl")
public class MysqlSequenceServiceImpl implements MysqlSequenceService {
@Resource
private MysqlSequenceFactory mysqlSequenceFactory;
@Override
public String getNextSequnce(String seqName) {
String nextSeq = String.valueOf(mysqlSequenceFactory.getNextVal(seqName));
String maxSequence = SCMUtil.getScmPropValidResult(ScmConstants.MAXSEQUENCE);
if(StringUtil.isBlankNew(maxSequence)){
maxSequence="10000";
}
return StringUtil.leftFillZero(nextSeq,maxSequence.length());
}
/**
* 功能描述: 获取下一个日志ID<br>
*
* @param: []
* @return: java.lang.String
* @author: 88449366
* @Date: 2019/10/15
*/
@Override
public String getNextLogId(){
String seqName = new SimpleDateFormat("yyyyMMdd").format(DateUtil.getNow())+"LOG";
String seqNum = getNextSequnce(seqName);
return seqName+seqNum;
}
/**
* 功能描述: 获得业务表主键<br>
*
* @param: [type]
* @return: java.lang.String
* @author: 88449366
* @Date: 2019/10/15
*/
@Override
public String getNextCode(String type){
String seqName = new SimpleDateFormat("yyyyMMdd").format(DateUtil.getNow())+type;
String seqNum = getNextSequnce(seqName);
return seqName+seqNum;
}
/**
* 获取支付请求单号
* @param bizSubCateg
* @param distributeSys
* @return
*/
@Override
public String getNextPayReqNo(String bizSubCateg,String distributeSys) {
String seqTime = new SimpleDateFormat("yyyyMMdd").format(DateUtil.getNow());
String seqName = new SimpleDateFormat("yyyyMMdd").format(DateUtil.getNow())+"payReqNo";
String nextSeq = String.format("%04d",mysqlSequenceFactory.getNextVal(seqName));
return bizSubCateg+distributeSys+seqTime+nextSeq;
}
}
以上就是写好的Service,仅仅做个记录,防止自己以后可能要用到。
版权声明:本文为DAI_Zeekin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。