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 {

    public LogUtil() {
    }

//    private static final String LOG_LEVEL = StringUtil.isBlank(Globals.LOG_LEVEL) ? Globals.DEFAULT_LOG_LEVEL : Globals.LOG_LEVEL; //默認LOG級別
    private static DBUtil dao = DBUtil.me();
    private static final String LOG_DEFAULT = "LOG";
    private static final String LOG_WARNING = "WARN";
    private static final String LOG_ERROR = "ERROR";

    private static String logs = "";

    public static LogUtil me() {
        return lazyInit.INITIALIZATION;
    }
    private static class lazyInit {
        private static final LogUtil INITIALIZATION = new LogUtil();
    }
    public void log(Object log) {
//        System.out.println(log.getClass().getName());
        String string = "";
        switch (log.getClass().getName()){
            case "java.lang.String":
                logs = "["+LOG_DEFAULT+" ] "+getLog(2)+"\t"+String.valueOf(log == null ? "" : log);
                System.out.println(logs);
                break;
            case "java.util.Arrays$ArrayList":
                for(Object o : (List)log) {
                   log(o);
                }
                break;
            case "java.lang.StackTraceElement":
                logs = "\t"+String.valueOf(log == null ? "" : log);
                System.out.println(logs);
                break;
            default:
                logs = "["+LOG_DEFAULT+" ] "+getLog(2)+"\t"+String.valueOf(log == null ? "" : log);
                System.out.println(logs);
        }
    }

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

    public void error(Exception log) {
        logs = "["+LOG_ERROR+" ] "+getLog(2)+"\t"+String.valueOf(log == null ? "" : log);
        System.out.println(logs);
        save();
        log(Arrays.asList(log.getStackTrace()));
    }



    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))+")";
    }

    private void save() {
        //Persistence
    }

}



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下次再完善吧。

打印一下結果,似乎沒毛病


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章