All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lin.faker.Faker Maven / Gradle / Ivy

There is a newer version: 1.0.8
Show newest version
package com.lin.faker;

import com.lin.asserts.Asserts;
import com.lin.datatype.DataType;
import com.lin.helper.DatabaseHelper;
import com.lin.logger.Logger;
import com.lin.logger.LoggerFactory;
import com.lin.mapping.DataTypeMapping;
import com.lin.random.RandomData;
import com.lin.utils.FlyweightUtils;
import com.lin.utils.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.lin.constant.Constants.JDBC_H2;
import static com.lin.constant.Constants.JDBC_SQL_SERVER;

/**
 * 数据伪造器
 * @author lkmc2
 * @since 1.0.0
 */
public final class Faker {

    /** 数据库表名 **/
    private String tableName;

    /** 插入数据条数 **/
    private int count;

    /** 总的插入行数 **/
    private int totalCount = 0;

    /** 是否插入数据到数据库 **/
    private boolean isInsertDataToDb;

    /** 存储属性的同步Map **/
    private final Map PARAM_MAP;

    /** 日志记录器 **/
    private static final Logger LOGGER = LoggerFactory.getLogger(Faker.class);


    // 初始化参数存储Map和数据类型映射Map/
    {
        PARAM_MAP = Collections.synchronizedMap(new LinkedHashMap(10));
    }

    private Faker() {
    }

    /** 静态单例 **/
    private static final class FakerHolder {
        private static final Faker INSTANCE = new Faker();
    }

    /**
     * 获取Facker类实例,并设置数据库表名
     * @param tableName 数据库表名
     * @return 数据伪造器
     */
    public static Faker tableName(String tableName) {
        return FakerHolder.INSTANCE.setTableName(tableName);
    }

    /**
     * 设置数据库表名
     * @param tableName 数据库表名
     * @return 数据伪造器
     */
    private Faker setTableName(String tableName) {
        this.tableName = tableName;

        // 重置变量值
        resetVariable();

        return this;
    }

    /**
     * 重置变量值
     */
    private void resetVariable() {
        this.PARAM_MAP.clear();
        this.count = 0;
        this.totalCount = 0;
        this.isInsertDataToDb = false;
    }

    /**
     * 添加参数
     * @param paramName 参数名
     * @param paramType 参数类型
     * @return 数据伪造器
     */
    public Faker param(String paramName, Object paramType) {
        PARAM_MAP.put(paramName, paramType);
        return this;
    }

    /**
     * 设置数据库插入条数
     * @param count 插入条数
     * @return 数据伪造器
     */
    public Faker insertCount(int count) {
        this.count = count;
        return this;
    }

    /**
     * 不指定SQL生成和插入操作
     */
    public void ignored() {
    }

    /**
     * 只是显示生成的SQL,不插入数据到数据库
     * 该方法用于检测生成的数据是否正确
     */
    public void onlyShowSql() {
        // 设置不插入数据到数据库
        this.isInsertDataToDb = false;

        // 执行主要逻辑
        executeMainLogic();
    }

    /**
     * 执行插入数据到数据库,并显示执行的SQL语句
     */
    public void execute() {
        // 设置插入数据到数据库
        this.isInsertDataToDb = true;

        // 执行主要逻辑
        executeMainLogic();
    }

    /**
     * 执行主要逻辑
     */
    private void executeMainLogic() {
        // 1.检查参数是否传入成功
        checkParams();

        // 2.执行生成并执行SQL语句的逻辑
        generateAndExecuteSql();
    }

    /**
     * 检查参数是否传入成功
     */
    private void checkParams() {
        Asserts.isTrue(StringUtils.isNotEmpty(tableName), "数据库表名不能为空");

        // 属性数必须大于等于1
        Asserts.isTrue(PARAM_MAP.size() >= 1, "必须设置一条以上属性值,需要使用param(paramName,paramType)方法设置");

        // 插入条数必须大于等于1
        Asserts.isTrue(count >= 1, "插入条数应大于等于1条,需要使用insertCount(int)方法设置插入条数");
    }

    /**
     * 执行生成并执行SQL语句的逻辑
     */
    private void generateAndExecuteSql() {
        // 提交事务时生成的 sql 数量
        int commitSize = 1000;

        try {
            // 如果需要插入到数据库
            if (this.isInsertDataToDb) {
                // 开始事务
                DatabaseHelper.beginTransaction();
            }

            for (int i = 0; i < count; i++) {
                // 生成参数名和参数值列表
                String[] paramNameAndValue = generateParamNameAndValue();

                // 参数名,如:name,age,sex,birthday
                String paramName = paramNameAndValue[0];
                // 参数值,如:'jack','13','0','1999-9-9 12:12:12'
                String paramValue = paramNameAndValue[1];

                // 拼接成 sql insert 语句
                String sql = String.format("insert into %s(%s) values(%s)", tableName, paramName, paramValue);
                // 将 sql 转换成对应数据库类型的 sql
                sql = handleWithSpecifyDatabase(paramName, paramValue, sql);

                LOGGER.info(sql);

                // 如果需要插入到数据库,执行生成的 sql 语句
                if (this.isInsertDataToDb) {
                    // 执行sql,获取受影响行数
                    int effectCount = DatabaseHelper.executeUpdate(sql);

                    // 统计总的插入条数
                    this.totalCount += effectCount;

                    if (this.totalCount % commitSize == 0) {
                        // 每执行 1000 条 sql 就提交事务
                        DatabaseHelper.commitBatchTransaction();
                    }
                }
            }

            // 如果需要插入到数据库
            if (this.isInsertDataToDb) {
                // 当还有未提交的 sql 时,提交事务
                if (this.totalCount % commitSize != 0) {
                    // 提交事务
                    DatabaseHelper.commitTransaction();
                }
            }

            if (this.isInsertDataToDb) {
                // 插入数据到数据库
                LOGGER.print(String.format("成功插入[ %s ]条数据",this.totalCount));
            } else {
                // 不插入数据到数据库
                LOGGER.print(String.format("成功生成[ %s ]条数据", this.count));
            }
        } catch (Exception e) {
            e.printStackTrace();

            // 如果需要插入到数据库
            if (this.isInsertDataToDb) {
                // 事务回滚
                DatabaseHelper.rollbackTransaction();
            }
        }
    }

    /** 日期格式化器 **/
    private ThreadLocal dateFormatter = new ThreadLocal() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    /**
     * 将 sql 转换成对应数据库类型的 sql
     * @param paramName 参数名列表字符串
     * @param paramValue 参数值列表字符串
     * @param sql 默认生成的 sql 语句
     * @return 对应数据库类型的 sql
     */
    private String handleWithSpecifyDatabase(String paramName, String paramValue, String sql) {
        // 获取数据库 url 地址
        String url = DatabaseHelper.getDataSource().getUrl();

        if (url != null && !url.isEmpty()) {
            // 根据 url 判断是否 sql server 数据库,进行特别处理
            if (url.contains(JDBC_SQL_SERVER)) {
                sql = String.format("insert into [dbo].[%s](%s) values(%s)", tableName, paramName, paramValue);
            }

            // 根据 url 判断是否 H2 数据库,进行特别处理
            if (url.contains(JDBC_H2)) {
                // 将参数值列表字符串拆分成数组
                String[] valueArray = paramValue.split(",");

                StringBuilder sb = new StringBuilder();

                for (String value : valueArray) {
                    try {
                        dateFormatter.get().parse(value.replace("'", ""));
                        // 可以解析成日期的字符串加上 parsedatetime 函数处理
                        sb.append(String.format("parsedatetime(%s, 'yyyy-MM-dd HH:mm:ss')", value));
                    } catch (ParseException e) {
                        // 不可以解析成日期的字符串取原来的值
                        sb.append(value);
                    }
                    sb.append(",");
                }

                // 生成对应的参数值列表字符串
                paramValue = StringUtils.deleteLastComma(sb);

                sql = String.format("insert into %s(%s) values(%s)", tableName, paramName, paramValue);
            }
        }

        return sql;
    }

    /**
     * 生成参数名和参数值列表数组
     * 数组返回值示范:
     * 数组【0】:name,age,sex,birthday
     * 数组【1】:'jack','13','0','1999-9-9 12:12:12'
     * @return 参数名和参数值列表数组
     */
    private String[] generateParamNameAndValue() {
        StringBuilder paramNameSb = new StringBuilder();
        StringBuilder paramValueSb = new StringBuilder();

        // 使用Map生成参数
        for (Map.Entry entry : PARAM_MAP.entrySet()) {
            String paramName = entry.getKey();
            Object paramType = entry.getValue();

            // 添加参数名
            paramNameSb.append(paramName.trim()).append(",");

            // 创建参数值(添加参数值)
            createParamValue(paramType, paramValueSb);
        }

        // 所有参数名,如:name,age,sex,birthday
        String paramNames = StringUtils.deleteLastComma(paramNameSb);

        // 所有参数值,如:'jack','13','0','1999-9-9 12:12:12'
        String paramValues = StringUtils.deleteLastComma(paramValueSb);

        return new String[]{ paramNames, paramValues };
    }

    /**
     * 创建参数值
     * @param paramType 参数类型
     * @param paramValueSb 参数值字符串
     */
    private void createParamValue(Object paramType, StringBuilder paramValueSb) {
        Asserts.isTrue(paramType instanceof DataType
                        || paramType instanceof RandomData
                        || RandomData.class.isAssignableFrom((Class) paramType),
                "传入的paramType必须是DataType枚举值或实现RandomData接口的类");

        Object target;

        if (paramType instanceof RandomData) {
            // 实现了RandomData接口
            target = paramType;
        } else {
            // 传入的是DataType枚举或Class类型
            if (paramType instanceof DataType) {
                // 是数据类型枚举
                DataType dataType = (DataType) paramType;

                // 从数据类型Map中获取对应的随机生成器类型
                paramType = DataTypeMapping.getMapping(dataType);
            }

            // 获取目标类的单例对象(实例化目标类)
            target = FlyweightUtils.getInstance((Class) paramType);
        }

        // 生成随机数据(核心语句)
        Object randomData = ((RandomData) target).next();

        // 将随机数据转换成字符串
        String data = (randomData instanceof String) ? (String) randomData : String.valueOf(randomData);

        // 在字符串两边加上单引号,然后加上逗号
        paramValueSb.append(StringUtils.addSingleQuotesAround(data)).append(",");
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy