背景
回想平时我们测试,需要数据就在数据库一顿猛操作也没达到所谓的乱数假文,但是Faker很好的为我们解决了这个问题,伪造的数据接近真实数据。但是我们也不能每次都去一个一个数据的生成,这样会叠加代码量,工作量也增加了,于是写了一个工具类,将faker相关的方法以及方法需要的参数和参数类型存入了数据库中,前端只要配置好相对应的数据就能生成伪造数据
githup地址
技术点
泛型+反射
Faker
基本用法
用法为:Faker.**.**
Faker faker = Faker.instance(Locale.CHINA);//.instance(Locale.CHINA)指定为中文
//默认英语,如下:
//Faker faker = new Faker();
//生成数据(生成随机电话号码)
//faker.phoneNumber().phoneNumber();
System.err.println("生成的随机电话号码为:"+faker.phoneNumber().phoneNumber());
Faker支持多种语言,80+个基础方法,约500种假数据类型(常用的有320种左右,记不清楚了,写入数据库就知道了),详情请看githup地址
控制台输出结果
生成的随机电话号码为:11132037344
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
maven 依赖
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
数据库表结构
数据库字段以及字段类型由于是测试,可根据实际需求修改。
utilFaker,主要用于存入Faker一级二级方法(faker.phoneNumber().cellPhone())一级为phoneNumber()二级为cellPhone()
util_faker_parameterinfo主要是存入最终方法的参数以及参数类型
如faker.number().randomDouble(int maxNumberOfDecimals, int min, int max)三个int类型的参数,参数主要是设置默认参数
JAVA实现
MockItem参数类(包含调用的方法,参数以及输出时参数对应的key)
@Data
public class MockItem{
/**
* @ 名称对应的faker一级方法.二级方法(number.randomDigit)
*/
private String methodNames;
/**
* @ 生成数据调用方法对应的参数,前段传入的参数
*/
public List<MockItemParamters> inParameters;
/**
* @ 返回数据时的key
*/
private String key;
}
MockItemParamters(调用方法时传入的参数以及参数类型)
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MockItemParamters {
/**
* @ 名称对应Params参数
*/
private Object Params;
/**
* @ 名称对应Params参数类型
*/
Class<?> clazz;
}
最终实现的工具类GenerateData
public class GenerateData{
/**
* TODO
* @param: faker
* @param: mockItem 生成数据是调用的方法,方法需要的参数及类型以及输出数据时的key
* @param: number 生成假数据的条数
* @param: clazz 需要输出的实体
*/
public static <T> List<T> generatList(Faker faker, List<MockItem> mockItem, int number, Class<T> clazz) throws Exception {
List<T> listResult = new ArrayList<>();
for(int i = 0; i < number; i++){
listResult.add(i,generateEntity(faker,mockItem,clazz));
}
return listResult;
}
public static List<Map<String, Object>> generatListMap(Faker faker,List<MockItem> mockItem,int number){
List<Map<String, Object>> listResult = new ArrayList<>();
for(int i=0;i<number;i++){
listResult.add(i,generateMap(faker,mockItem));
}
return listResult;
}
/**
* TODO
* @Description: 一次生成一条Map假数据
* @return: listResultdata
*/
public static Map<String, Object> generateMap(Faker faker,List<MockItem> mockItem){
Map<String,Object> listResultdata = new HashMap<>();
try {
for (MockItem m : mockItem) {
listResultdata.put(m.getKey(),generateSingleData(faker,m));
}
}
catch (Exception e){
throw new RuntimeException("Data generation failed", e);
}
return listResultdata;
}
/**
* TODO
* @Description: 一次生成一条数据,核心在于利用BeanUtils.setProperty反射机制
* @return: T
*/
public static <T> T generateEntity(Faker faker,List<MockItem> mockItem, Class<T> clazz){
try {
T result = clazz.newInstance();
for (MockItem mock : mockItem) {
Object value = generateSingleData(faker, mock);
BeanUtils.setProperty(result, mock.getKey(), value);
}
return result;
}
catch (Exception e){
throw new RuntimeException("Data generation failed", e);
}
}
/**
* TODO
* @Description: 调用Faker方法一次生成一个数据
* @return: (T)
*/
public static Object generateSingleData(Faker faker,MockItem mockItem) throws Exception{
String[] methods = mockItem.getMethodNames().split("\\.");
if(methods.length != 2) {
throw new RuntimeException("methodNames is not invaild");
}
Object firstMethod = faker.getClass().getMethod(methods[0]).invoke(faker);//获取到faker第一个方法的调用句柄
Class<?> firstMethodClass = firstMethod.getClass();
if(mockItem.getInParameters() != null) {
Class<?>[] clazz = typeConversion(mockItem.getInParameters());
Object[] paramets = parameterConversion(mockItem.getInParameters());
//getMethod的第二个参数parameterTypes是按声明顺序标识该方法形参类型。
return firstMethodClass.getMethod(methods[1], clazz).invoke(firstMethod, paramets);
}
return firstMethodClass.getMethod(methods[1]).invoke(firstMethod);
}
/**
* TODO
* @Description: 根据调用者提供的数据类型进行相对应的转换(类型转换)主要用在getMethod的第二个参数parameterTypes需要声明方法形参类型
* @param: mockItemParamters 参数类包含参数及参数类型
* @return: Class<?>[]
*/
public static Class<?>[] typeConversion(List<MockItemParamters> mockItemParamters){
Class<?>[] clazz = new Class<?>[mockItemParamters.size()];
//遍历获取形参类型以及转换参数
int i = 0;
for(MockItemParamters m : mockItemParamters){
if(int.class == m.getClazz() || Integer.class == m.getClazz()){
clazz[i] = int.class;
}else if(boolean.class == m.getClazz() || Double.class == m.getClazz()){
clazz[i] = boolean.class;
}else if(long.class == m.getClazz() || Long.class == m.getClazz()){
clazz[i] = long.class;
}else if(byte.class == m.getClazz() || Byte.class == m.getClazz()){
clazz[i] = byte.class;
}else if(float.class == m.getClazz() || Float.class == m.getClazz()){
clazz[i] = float.class;
}else if(double.class == m.getClazz() || Double.class == m.getClazz()){
clazz[i] = double.class;
}else if(short.class == m.getClazz() || Short.class == m.getClazz()){
clazz[i] = short.class;
}else{
clazz[i] = String.class;
}
i++;
}
return clazz;
}
/**
* TODO
* @Description: 根据调用者提供的数据类型进行相对应的转换(参数类型转换)
* @param: mockItemParamters参数类包含参数及参数类型
* @return: Class<?>[]
*/
public static Object[] parameterConversion(List<MockItemParamters> mockItemParamters){
Object[] paramets = new Object[mockItemParamters.size()];
//遍历获取形参类型以及转换参数
int i = 0;
for(MockItemParamters m : mockItemParamters){
if(int.class == m.getClazz() || Integer.class == m.getClazz()){
//int a = ConvertUtils.convert(m.getParams(),int.class,true);
paramets[i] = (Integer.parseInt(m.getParams().toString()));
}else if(boolean.class == m.getClazz() || Double.class == m.getClazz()){
paramets[i] = (Boolean.parseBoolean(m.getParams().toString()));
}else if(long.class == m.getClazz() || Long.class == m.getClazz()){
paramets[i] = (Long.parseLong(m.getParams().toString()));
}else if(byte.class == m.getClazz() || Byte.class == m.getClazz()){
paramets[i] = (Byte.parseByte(m.getParams().toString()));
}else if(float.class == m.getClazz() || Float.class == m.getClazz()){
paramets[i] = (Float.parseFloat(m.getParams().toString()));
}else if(double.class == m.getClazz() || Double.class == m.getClazz()){
paramets[i] = (Double.parseDouble(m.getParams().toString()));
}else if(short.class == m.getClazz() || Short.class == m.getClazz()){
paramets[i] = (Short.parseShort(m.getParams().toString()));
}else{
paramets[i] = (m.getParams().toString());
}
i++;
}
return paramets;
}
public static void main(String[] args) throws Exception {
Faker faker = Faker.instance(Locale.CHINA);
List<MockItem> mockItemList = new ArrayList<>();
//参数
List<MockItemParamters> mockItemParamters = new ArrayList<>();
MockItemParamters MockItemParamters = new MockItemParamters();
MockItemParamters.setParams(2);
MockItemParamters.setClazz(int.class);
MockItemParamters MockItemParamters1 = new MockItemParamters();
MockItemParamters1.setParams(2);
MockItemParamters1.setClazz(int.class);
MockItemParamters MockItemParamters2 = new MockItemParamters();
MockItemParamters2.setParams(99);
MockItemParamters2.setClazz(int.class);
mockItemParamters.add(MockItemParamters);
mockItemParamters.add(MockItemParamters1);
mockItemParamters.add(MockItemParamters2);
MockItem mockItem = new MockItem();
mockItem.setMethodNames("name.fullName");
mockItem.setKey("name");
MockItem mockItem1 = new MockItem();
mockItem1.setMethodNames("number.randomDouble");
mockItem1.setInParameters(mockItemParamters);
mockItem1.setKey("value");
mockItemList.add(mockItem);
mockItemList.add(mockItem1);
//输入的参数
System.out.println("输入的参数~~~~~~~~~~~~~~~~~"+mockItemList);
//根据实体类生成
List<***> **= generatList(faker,mockItemList,10, ***.class);
System.out.println("根据实体映射输出的list结果~~~~~~~~~~~~~~~~~"+**);
//生成map类型
List<Map<String,Object>> listMap = generatListMap(faker, mockItemList, 10);
System.out.println("输出的listMap结果~~~~~~~~~~~~~~~~~"+listMap );
}
}
输出结果
输入的参数~~~~~~~~~~~~~~~~~[MockItem(methodNames=name.fullName, inParameters=null, key=name), MockItem(methodNames=number.randomDouble, inParameters=[MockItemParamters(Params=2, clazz=int), MockItemParamters(Params=2, clazz=int), MockItemParamters(Params=99, clazz=int)], key=value)]
根据实体映射输出的10条结果~~~~~~**实体类名称~~~~~~~~~~~[**(name=苏梓晨, value=39.76), **(name=叶昊然, value=47.0), **(name=张笑愚, value=67.61), **(name=黎峻熙, value=77.59), **(name=覃彬, value=14.1), **(name=贺展鹏, value=8.53), **(name=覃致远, value=77.93), **(name=谢煜城, value=35.23), **(name=孙风华, value=29.26), **(name=赖志泽, value=15.06)]
输出的10条Map结果~~~~~~~~~~~~~~~~~[{name=崔天磊, value=78.89}, {name=邱思聪, value=44.28}, {name=陆伟宸, value=57.53}, {name=宋远航, value=19.53}, {name=曾炫明, value=82.85}, {name=周鹏飞, value=82.98}, {name=谭文博, value=78.05}, {name=陈瑾瑜, value=15.98}, {name=史明辉, value=56.29}, {name=尹正豪, value=13.07}]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
将Faker中的方法以及参数类型输出到数据库中
根据自己框架把 super.save修改为自己的插入数据的方法即可(insert语句)
public void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Faker faker = Faker.instance( Locale.CHINA);
Field[] fileds = faker.getClass().getDeclaredFields();
for( Field f1:fileds ){
UtilFakerPO utilFakerPO = new UtilFakerPO();
utilFakerPO.setCreateTime(new Date());
utilFakerPO.setEhName(f1.getName());
utilFakerPO.setMthod(f1.getName());
utilFakerPO.setParentId(0l);
super.save(utilFakerPO);
UtilFakerParameterinfoVo details = ConvertUtils.convertIgnoreNull(utilFakerPO, UtilFakerParameterinfoVo.class);
if(!"fakeValuesService".equals(f1.getName())){
String name = f1.getName();
if("randomService".equals(f1.getName())){
name = "random";
}else if("dateAndTime".equals(f1.getName())){
name = "date";
}
Object object = faker.getClass().getMethod(name).invoke(faker);
Method[] methods = object.getClass().getDeclaredMethods();
for (Method method:methods){
String methodName = method.getName();
UtilFakerPO utilFakerchild = new UtilFakerPO();
utilFakerchild.setCreateTime(new Date());
utilFakerchild.setEhName(methodName);
utilFakerchild.setMthod(name+"."+methodName);
utilFakerchild.setParentId(details.getId());
super.save(utilFakerchild);
UtilFakerParameterinfoVo detailschild = ConvertUtils.convertIgnoreNull(utilFakerchild, UtilFakerParameterinfoVo.class);
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> clas:parameterTypes){
String parameterName = clas.getSimpleName();
UtilFakerParameterinfoPO utilFakerParameterinfoPO = new UtilFakerParameterinfoPO();
utilFakerParameterinfoPO.setCreateTime(new Date());
utilFakerParameterinfoPO.setDataType(parameterName);
utilFakerParameterinfoPO.setUtilFakerId(detailschild.getId());
super.save(utilFakerParameterinfoPO);
}
}
}
}
};
上面代码中类型转换与参数转换应该有更好的办法,我暂时没有查到,如果大神有更好的方法求指教,写下这篇文章纯属为了学习,记录。同时也希望能给他人提供帮助。