java,mysql触发器,redis生成流水号(yyyyMM000)

最近又遇到需要根据日期生成流水号的业务,然后记录了几种生成方法,一个是通过 java 代码,一个是数据库的触发器,还有是通过 redis。下面是代码:

通过 java 生成简易流水:

    /**
     * 通过日期和生成的流水号拼接
     * @param maxCount 已经生成的个数
     * @return
     */
    public static String recountNew(int maxCount) {
        if (maxCount < 0) {
            return null;
        }
        //201707999
        String str = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        String countStr = str + num(maxCount, 3, 3);
        System.out.println("合同编号:" + Long.valueOf(countStr));
        return countStr;
    }
<span class="hljs-comment">/**
 * 生成流水号
 * <span class="hljs-doctag">@param</span> current 当前生成个数
 * <span class="hljs-doctag">@param</span> max 最大整数位
 * <span class="hljs-doctag">@param</span> min 最小整数位
 * <span class="hljs-doctag">@return</span> 生成的流水号
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">num</span><span class="hljs-params">(<span class="hljs-type">int</span> current, <span class="hljs-type">int</span> max, <span class="hljs-type">int</span> min)</span> {
    current++;
    <span class="hljs-type">NumberFormat</span> <span class="hljs-variable">numberFormat</span> <span class="hljs-operator">=</span> NumberFormat.getInstance();
    <span class="hljs-comment">//设置是否使用分组</span>
    numberFormat.setGroupingUsed(<span class="hljs-literal">false</span>);
    <span class="hljs-comment">//设置最大整数位数</span>
    numberFormat.setMaximumIntegerDigits(max);
    <span class="hljs-comment">//设置最小整数位数</span>
    numberFormat.setMinimumIntegerDigits(min);
    <span class="hljs-keyword">return</span> numberFormat.format(current);
}

通过数据触发器实现:

主要逻辑:以 201906001 为例,根据当前日期 201606 获取流水号最大的一个,保存到 n。然后把流水号加 1,再和当前日期 201906 拼接到一起

		CREATE TABLE orders (
			orders_id INT (10) PRIMARY KEY,
			customer_name VARCHAR (100)
		);
	<span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TRIGGER</span> tr_orders_id;

	<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TRIGGER</span> tr_orders_id BEFORE <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">ON</span> orders <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">EACH</span> <span class="hljs-type">ROW</span>
	<span class="hljs-keyword">BEGIN</span>
		<span class="hljs-keyword">DECLARE</span>
			n <span class="hljs-type">INT</span>;
	<span class="hljs-keyword">SELECT</span>
		IFNULL(<span class="hljs-built_in">max</span>(<span class="hljs-keyword">RIGHT</span>(orders_id, <span class="hljs-number">3</span>)), <span class="hljs-number">0</span>) <span class="hljs-keyword">INTO</span> n
	<span class="hljs-keyword">FROM</span>
		orders
	<span class="hljs-keyword">WHERE</span>
		mid(orders_id, <span class="hljs-number">1</span>, <span class="hljs-number">6</span>) <span class="hljs-operator">=</span> DATE_FORMAT(now(), <span class="hljs-string">'%Y%m'</span>);
	<span class="hljs-keyword">SET</span> NEW.orders_id <span class="hljs-operator">=</span> concat(
		DATE_FORMAT(now(), <span class="hljs-string">'%Y%m'</span>),
		<span class="hljs-keyword">RIGHT</span> (<span class="hljs-number">001</span> <span class="hljs-operator">+</span> n, <span class="hljs-number">3</span>)
	);
	<span class="hljs-keyword">END</span>;

	<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> orders <span class="hljs-keyword">VALUES</span> (<span class="hljs-number">0</span>, <span class="hljs-string">'jack'</span>);
	<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> orders <span class="hljs-keyword">VALUES</span> (<span class="hljs-number">0</span>, <span class="hljs-string">'jack'</span>);

redis 实现(采用)

主要利用 StringRedisTemplate 来操作 redis,写在业务层,需要使用直接注入就行。这是没有详细的说明配置 StringRedisTemplate,下面代码会有问题,只是知道有这个方法,有用的时候,自己去写一下就好了。


/**
 * @version V1.0
 * @Authoer CX
 * @Since:2019/5/20
 */
public interface NumberGenService {
<span class="hljs-comment">/**
 * 根据code生成编号
 * 例:NB000001
 * <span class="hljs-doctag">@param</span> code 前缀
 * <span class="hljs-doctag">@return</span> 编号
 */</span>
<span class="hljs-title class_">String</span> <span class="hljs-title function_">generateNumber</span>(<span class="hljs-title class_">String</span> code);

<span class="hljs-comment">/**
 * 根据code及年月生成编号
 * 例子:NB201905000001
 * <span class="hljs-doctag">@param</span> code 前缀
 * <span class="hljs-doctag">@return</span> 编号
 */</span>
<span class="hljs-title class_">String</span> <span class="hljs-title function_">generateNumberByMonth</span>(<span class="hljs-title class_">String</span> code);


<span class="hljs-comment">/**
 * 根据code及年月生成编号
 * 例子:NB20190508000001
 * <span class="hljs-doctag">@param</span> code 前缀
 * <span class="hljs-doctag">@return</span> 编号
 */</span>
<span class="hljs-title class_">String</span> generateNumberByDay (<span class="hljs-title class_">String</span> code);

}

import com.cloudkeeper.confinement.main.service.NumberGenService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @version V1.0
 * @Authoer CX
 * @Since:2019/5/20
 */
@Service
public class NumberGenServiceImpl implements NumberGenService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final int LENGTH = 6;

    private static final String MONTH_FORMAT = "yyyyMM";

    private static final String DAY_FORMAT = "yyyyMMdd";

    public String generateNumber (String code) {
        return getNumber(code, "");
    }

    public String generateNumberByMonth (String code) {
        return getNumber(code, new SimpleDateFormat(MONTH_FORMAT).format(new Date()));
    }

    public String generateNumberByDay (String code) {
        return getNumber(code, new SimpleDateFormat(DAY_FORMAT).format(new Date()));
    }


    private String getNumber(String code, String month) {
        code += month;
        Long number = stringRedisTemplate.opsForValue().increment("" + ":" + code);
        return code + StringUtils.leftPad(number.toString(), LENGTH, '0');
    }

}

总结

以上是总结的几种实现方式,公司采用的是通过 redis 的自增来实现的,可以避免并发时生成相同编号的问题。通过 java 生成,在保存时会出现相同编号的问题。