业务场景中经常有需要生成单号,单号是连续递增的,不能中断
例如:生成一个单号,单号规则是,四位流水号,初始值:0001
实现步骤
-
从数据库中查询最大的值
基于mybatis-plus的QueryWrapper查询
-
在这个最大值基础上加一
使用hutool的工具StrUtil类中的padPre方法,补充字符串以满足指定长度
添加依赖
<!-- hutool依赖 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.19</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
代码实现
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
/**
* @date 2024/2/21
*/
@Component
public class NumberGenerateUtil {
/**
* 单号生成(纯数字,不走redis)
*
* @param func
* @param length
* @param baseMapper
* @param <T>
* @return
*/
public <T> String genAutoNumber(SFunction<T, ?> func, int length, BaseMapper<T> baseMapper) {
String maxValue = getMaxValue(func, baseMapper);
if (maxValue != null) {
return numberAdd(maxValue);
} else {
return StrUtil.padPre("1", length, "0");
}
}
/**
* 获取数据库中字段的最大值
*
* @param func
* @param baseMapper
* @param <T>
* @return
*/
private <T> String getMaxValue(SFunction<T, ?> func, BaseMapper<T> baseMapper) {
String property = PropertyNamer.methodToProperty(SerializedLambda.resolve(func).getImplMethodName());
String column = StrUtil.toUnderlineCase(property);
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
queryWrapper.select("max(" + column + ") as " + property);
queryWrapper.last("for update");
T entity = baseMapper.selectOne(queryWrapper);
if (entity == null) {
return null;
}
Object propertyValue = getValueByPropName(entity, property);
if (propertyValue == null) {
return null;
} else {
return propertyValue.toString();
}
}
/**
* 编号加1
*
* @param number
* @return
*/
private static String numberAdd(String number) {
int addend = Integer.parseInt(number);
addend = addend + 1;
String s = String.valueOf(addend);
if (s.length() > number.length()) {
throw new RuntimeException("编号长度超出限制");
}
return StrUtil.padPre(s, number.length(), "0");
}
/**
* 通过属性名获取对象的属性值
*
* @param object
* @param propName
* @return
*/
public String getValueByPropName(Object object, String propName) {
String value = null;
try {
// 通过属性获取对象的属性
// getDeclaredFields() 获得某个类的所有声明的字段,即包括public、private和proteced但不包括父类申明字段
// getClass() 是⼀个对象实例的⽅法,只有对象实例才有这个⽅法,具体的类是没有的
Field field = object.getClass().getDeclaredField(propName);
// 对象的属性的访问权限设置为可访问
// 允许获取实体类private的参数信息
field.setAccessible(true);
// 获取属性的对应的值
value = field.get(object).toString();
} catch (Exception e) {
return null;
}
return value;
}
}
项目中使用
为商品的number设置单号
import com.mamba.mybatisplus.entity.Product;
import com.mamba.mybatisplus.mapper.ProductMapper;
import com.mamba.mybatisplus.service.ProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mamba.mybatisplus.utils.NumberGenerateUtil;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import javax.annotation.Resource;
import java.util.concurrent.atomic.AtomicInteger;
/**
* <p>
* 商品表 服务实现类
* </p>
* @since 2024-02-21
*/
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
@Resource
private NumberGenerateUtil numberGenerateUtil;
@Resource
private ProductMapper productMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Product add(Product product) {
String number = numberGenerateUtil.genAutoNumber(Product::getNumber, 4, productMapper);
product.setNumber(number);
save(product);
return product;
}
}