这个需求很特别,比如只有脚本文件,而我要有选择的去筛选一些属性,然后制作成一个报表之类的。
如何达到良好的复用性和扩展性,因为本人比较菜,还有很多问题今后会继续修改并完善这篇文章,也希望有心人士可以和我沟通交流,感激不尽。
直接开撸。开撸之前要有整体架构的思想。
嗯,虽然只有几行代码,但是也是要设计的。
工具类: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下次再完善吧。