java反射之 解析sql脚本,映射到实体对象,面向对象思想,接口编程,java8流的使用等

这个需求很特别,比如只有脚本文件,而我要有选择的去筛选一些属性,然后制作成一个报表之类的。

如何达到良好的复用性和扩展性,因为本人比较菜,还有很多问题今后会继续修改并完善这篇文章,也希望有心人士可以和我沟通交流,感激不尽。

直接开撸。开撸之前要有整体架构的思想。

嗯,虽然只有几行代码,但是也是要设计的。

工具类:DateUtil,ObjectUtil,LogUtil

测试类:StringTest,Test

公共类:Globals

实体类:Star

接口:Sqls

实现类:SqlsImpl

注解:@Sql

达到的效果如下:解析一条sql脚本

insert into 'star' ('id','code','num','ex','character') values('6bfdf86056394ed1bc23c50b33df190b' , 1 , 999 , false , '周');

然后打印一个对象

[LOG ] 2018-06-28 17:05:37,324 ==> savvy.wit.zjj.test.StringTest:(main.35) Star{id='6bfdf86056394ed1bc23c50b33df190b', code=1, num=999, character=周, ex=false}


测试类,啥也没有,只是模拟几条数据测试一下。情景中可能会从sql脚本中读取一行行数据然后解析。

public class StringTest extends Test {


    public static void main(String[] args) {
//        Test.me().print();

        String insertSQL = "insert into 'star' ('id','code','num','ex','character') values('6bfdf86056394ed1bc23c50b33df190b' , 1 , 999 , false , '周');";
        String insertSQL1 = "insert into 'star' ('id','code','num','ex','character') values('asdagasdasdasdasdasdasddasdasd33' , 0 , 213 , true , '家');";
        List<String> sql = new ArrayList<>();
        sql.add(insertSQL);
        sql.add(insertSQL1);
        Sqls<Star> sqls = new SqlsImpl<>();
        Star star = sqls.parseInsert(insertSQL,Star.class);
        log.log(star.toString());
        List<Star> list = sqls.parseInsert(sql,Star.class);
        list.forEach(System.out::println);
        list.forEach(log::log);
    }
}



测试基类Test,很烦每次都要写一行log

public class Test{

    protected static LogUtil log = LogUtil.me();
    
}

注解Sql,干啥的呢,就是为了让程序知道,我应该去装配哪些属性

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Sql {

}

接口Sqls,暂时只写了insert脚本,好像也只有insert脚本才有数据? 

public interface Sqls<T> {

    /**
     *     insert  into `cms_site`(`id`,`site_name`,`site_sname`,`site_domain`,`site_path`,`site_css`,`is_static`,`location`,`opBy`,`opAt`,`delFlag`)
     *     values ('6bfdf86056394ed1bc23c50b33df190b','Eysh','Eysh','www.xyy277.cn','eysh','default',NULL,1,'1a38a92345bd44ed98648b9162b2d67a',1521901540,0);
     *     通过实体与sql的映射,返回
     */

    T parseInsert(String sql, Class clazz);

    List<T> parseInsert(List<String> sqls,  Class clazz);


}

Sqls实现类,实现一下

public class SqlsImpl<T> implements Sqls<T> {

    private LogUtil log = LogUtil.me();

    @Override
    public T parseInsert(String sqls, Class clazz) {
        T t = null;
        try {
            t = parseInsert(sqls, (T)clazz.newInstance() );
        }catch (InstantiationException e) {
            log.log("IllegalAccessException");
        }catch (IllegalAccessException e) {
            log.log("InstantiationException");
        }catch (ClassCastException e) {
            log.log(e.getMessage()+":"+e.getCause());
            e.printStackTrace();
        }
        return t;
    }

    @Override
    public List<T> parseInsert(List<String> sqls,  Class clazz) {
        List<T> list = new ArrayList<>();
        sqls.forEach(sql -> list.add( parseInsert(sql, clazz) ) );
        return list;
    }


    /**
     * insert into '***' ("sss","xxx") values ("aaa","bbb");
     * 单行sql解析
     * filed[]  values[]
     * @param sqls
     * @return
     */
    private T parseInsert(String sqls,T t) {
        try {
//            log.log("解析sql");
            sqls = sqls.replaceAll("'","").replaceAll("`","").replaceAll(" ","");
            String[] columns = sqls.substring(sqls.indexOf("(") + 1, sqls.indexOf(")")).split(",");
            String[] values = sqls.substring(sqls.lastIndexOf("(") + 1, sqls.lastIndexOf(")")).split(",");
            Field[] fields = t.getClass().getDeclaredFields();
            for(Field field : fields) {
                Sql sql = field.getAnnotation(Sql.class);
                if(sql == null) { //映射带Sql注解的列
                    continue;
                }
                for(int i = 0 ; i < columns.length; i++ ) {
                    if(field.getName().equals(columns[i])) {
//                        log.log("找到"+columns[i]+"列,并需要注入值......");
                        ObjectUtil.setValueByFieldName(t,columns[i],values[i]);
                        break; //一一对应 结束内层训话
                    }
                }
            }
        }catch (Exception e){
            log.log(e.getMessage());
            e.printStackTrace();
        } finally {
            return t;
        }
    }
}

ObjectUtil工具类,主要是通过反射机制获取和注入值,并实现一个常用数据类型的自动装配功能

public class ObjectUtil {

    private static LogUtil log = LogUtil.me();

    public static Object getValueByFiledName(Object o, String name) {
        try {
            Method[] methods = o.getClass().getMethods();
            List<Method> list = Arrays.asList(methods).parallelStream()
                    .filter(method -> ("get"+name).toLowerCase().equals(method.getName().toLowerCase()) )
                    .collect(Collectors.toList());
            return list.get(0).invoke(o);
        }catch (Exception e){
            log.log(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    public static Object getValueByFiled(Object o, Field field) {
        return getValueByFiledName(o,field.getName());
    }

    public static void setValueByFieldName(Object o, String name, Object value) {
        try {
            Method[] methods = o.getClass().getMethods();
            List<Method> list = Arrays.asList(methods).parallelStream()
                    .filter(method -> ("set"+name).toLowerCase().equals(method.getName().toLowerCase()) )
                    .collect(Collectors.toList());
            Method method = list.get(0);
            Class type = method.getParameterTypes()[0]; //set 参数列表 1
            autoInvoke(method,o,value,type);
        }catch (Exception e){
            log.log(e.getMessage());
            e.printStackTrace();
        }
    }

    public static void setValueByField(Object o, Field field, Object value) {
        setValueByFieldName(o,field.getName(),value);
    }

    /**
     * 常用数据类型自动装配
     * @param method set方法
     * @param o 装配对象
     * @param value 值
     * @param type 类型
     * @throws Exception
     */
    private static void autoInvoke(Method method, Object o, Object value, Class type) throws Exception {
        switch (type.getName()){
            case "java.lang.String":
                method.invoke(o,valueof(value)); break;
            case "int":
                method.invoke(o,Integer.parseInt(valueof(value))); break;
            case "java.lang.Integer":
                method.invoke(o,Integer.valueOf(valueof(value))); break;
            case "byte":
                method.invoke(o,Byte.parseByte(valueof(value))); break;
            case "java.lang.Byte":
                method.invoke(o,Byte.valueOf(valueof(value))); break;
            case "short":
                method.invoke(o,Short.parseShort(valueof(value))); break;
            case "java.lang.Short":
                method.invoke(o,Short.valueOf(valueof(value))); break;
            case "char":
                method.invoke(o,valueof(value).charAt(0)); break;
            case "java.lang.Character":
                method.invoke(o,Character.valueOf(valueof(value).charAt(0))); break;
            case "long":
                method.invoke(o,Long.parseLong(valueof(value))); break;
            case "java.lang.Long":
                method.invoke(o,Long.valueOf(valueof(value))); break;
            case "float":
                method.invoke(o,Float.parseFloat(valueof(value))); break;
            case "java.lang.Float":
                method.invoke(o,Float.valueOf(valueof(value))); break;
            case "double":
                method.invoke(o,Double.parseDouble(valueof(value))); break;
            case "java.lang.Double":
                method.invoke(o,Double.valueOf(valueof(value))); break;
            case "boolean":
                method.invoke(o,Boolean.parseBoolean(valueof(value))); break;
            case "java.lang.Boolean":
                method.invoke(o,Boolean.valueOf(valueof(value))); break;
        }
    }

    private static String valueof(Object value) {
        return String.valueOf(value);
    }

}


LogUtil 日志工具类,手写一个简单的日志输出

public class LogUtil {

    private static final String LOG_LEVEL = StringUtil.isBlank(Globals.LOG_LEVEL) ? Globals.DEFAULT_LOG_LEVEL : Globals.LOG_LEVEL; //默认LOG级别

    public static LogUtil me() {
        return new LogUtil();
    }

    public void log(Object log) {
        System.out.print("["+LOG_LEVEL+" ] "+getLog(2));
        System.out.println("\t"+String.valueOf(log == null ? "" : log));
    }

    private static String className(int i) {
        return Thread.currentThread().getStackTrace()[++i].getClassName();
    }

    private static String methodName(int i) {
        return Thread.currentThread().getStackTrace()[++i].getMethodName();
    }

    private static int lineNumber(int i) { return Thread.currentThread().getStackTrace()[++i].getLineNumber();}

    /**
     *
     * @return
     */
    public static String getLog() {
        return DateUtil.getNow()+"\t==>\t"+className(2)+":"+methodName(2);
    }

    public static String getLog(int i) {
        return DateUtil.getNow()+"\t==>\t"+className((i+1))+":("+methodName((i+1))+"."+lineNumber((i+1))+")";
    }
}


Globals全局参数配置,可以实现动态配置,这里略写

public class Globals {

    public static String LOG_LEVEL = "";

    public static String DEFAULT_LOG_LEVEL = "LOG";


    public static void initConfig(  ) {
        //do config
    }

最后是Star实体类

public class Star implements Serializable {

    static final String STAR = "☆";

    @Sql
    private String id;
    // 0 空格  1 ☆
    @Sql
    private int code;

    @Sql
    private Integer num;

    @Sql
    private char character;

    @Sql
    private boolean ex;

    public char getCharacter() {
        return character;
    }

    public void setCharacter(char character) {
        this.character = character;
    }

    public boolean getEx() {
        return ex;
    }

    public void setEx(boolean ex) {
        this.ex = ex;
    }

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getStar () {
        return code == 0 ? "  " : STAR ;
    }

    public String getStar (String star) {
        if( code == 0) {
            StringBuilder sb = new StringBuilder("");
            for (int i = 0; i < star.length(); i++) {
                sb.append(" ");
            }
            return  sb.toString();
        }else {
            return star;
        }
    }

    @Override
    public String toString() {
        return "Star{" +
                "id='" + id + '\'' +
                ", code=" + code +
                ", num=" + num +
                ", character=" + character +
                ", ex=" + ex +
                '}';
    }
}

工具类DateUtil

public class DateUtil {

    private static Random random = new Random();
    private static String pool = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM" ;

    /**
     * 根据系统时间随机获取一个编号
     * @param len
     * @return
     */
    public static Long createNumber(int len){
        StringBuilder sb = new StringBuilder(getNow("yyyyMMddHHmmss"));
        for(int i = 0 ; i++<len ;){
            sb.append(random(10));
        }
        return Long.parseLong(sb.toString());
    }

    /**
     * 根据系统时间随机获取一个编号
     * @param len
     * @return
     */
    public static String createCode(int len){
        StringBuilder sb = new StringBuilder(getNow("yyyyMMddHHmmss"));
        for(int i = 0 ; i++<len ;){
            sb.append( pool.charAt(random(pool.length()) ));
        }
        return sb.toString();
    }

    /**
     * 获取处理耗时
     * @param start
     * @param end
     * @return
     */
    public static String getTimeConsuming( long start,long end){
        long time=end-start;//计算插入耗时
        return "耗时:\t"+(time>10000?getTime(time, "mm分ss秒"):time+"毫秒");
    }

    /**
     * 获取当前系统时间
     * @param format 日期转化格式
     * 		       建议格式:yyyy-MM-dd HH:mm:ss
     * @return String 日期
     */
    public static String getNow(String format){
        return new SimpleDateFormat(format).format(new Date());
    }
    public static String getNow(){
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,S").format(new Date());
    }
    /**
     * 毫秒值转为 format类型的时间字符
     * @param time
     * @param format
     * @return
     */
    public static String getTime(long time,String format){
        Calendar calendar=Calendar.getInstance();
        calendar.setTimeInMillis(time);
        return new SimpleDateFormat(format).format(calendar.getTime());
    }

    /**
     * 获取系统当前毫秒值
     * @return
     */
    public static long getTime(){
        return System.currentTimeMillis();
    }

    /**
     * 获取时间
     *
     * @param str 字符串日期格式 2017-06-26 13:21:12
     * 			          统一解析格式 yyyy-MM-dd HH:mm:ss
     * @return date
     */
    public static Date getDate(String str){
        String format="yyyy-MM-dd HH:mm:ss";
        SimpleDateFormat sdf=new SimpleDateFormat(format);
        Date date=null;
        try {
            date=sdf.parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 获取随机数
     * @param ran
     * @return
     */
    public static int random(int ran){
        return random.nextInt(ran == 0 ? 1 : ran );
    }
}

几点扩展,解析不同的脚本格式,用不同的解析形式,正则表达式是否可以以逸待劳,用一个通用正则解决所有脚本。

哦,最后比如导入到Excel下次再完善吧。

打印一下结果,似乎没毛病


猜你喜欢

转载自blog.csdn.net/csdn277/article/details/80845277