【反射机制】与 【xml解析 】之 巧夺天工 —————— 开开开山怪

前情提要

这篇文章主要是解决标题是 MVC模式初体验 properties解析工具 与 Dao层的结合 在最后留的问题,就是怎样去优化下面这部分的代码:

public List<nativeModel> getNativeList() {
      sql = "SELECT id,name FROM sys_native_cnst";
      List<nativeModel> nativeList = new ArrayList<>();
      database db = new database();
    
      ResultSet rs = db.executeQuery(sql);
      try {
	      while(rs.next()) {
	              String id = rs.getString("id");
	          String name = rs.getString("name");
	              nativeList.add(new nativeInfo(id,name));
	          }
	   } catch (SQLException e) {
             e.printStackTrace();
           }
      return nativeList;
}

优化结果预期(自我觉得 优化就是做工具,把这一大堆搞成个工具,也适用于其他的model类)

public List<NativeModel> getNatives() {
   return  list(NativeModel.class);//相当于这个工具直接可以通过类 类型直接返回这个类的对象的List,
}

优化思路

对于上边的代码,我们是采用nativeModel来作为一个例子说明,我们所需要做的优化就是根据需要获得的ModelList的model类,对应到数据库中相应的model类的表,再根据表中拥有的字段来生成sql语句,并且产生一个关于这个类的对象的列表,其重点在于model类和表和其他信息的对应。

细化部分

那其实对于我们要完成的这个工具,在用的时候我们可以直接在这个关于获得各种 getXXXList 的函数中直接调用,而在调用时只需要一个参数就是model类,而我们的这个工具便可以通过这个类而找到对应的表,并且还可以获知表中存在哪些字段,这样我们便可以先产生一个sql语句,并且根据sql语句我们能从表中获取一个ResultSet(结果集),那么这个结果集就是表中每一条记录的集合,那之前说过,其实表中的一条记录对应的是每一个model类的一个对象,此时我们可以根据这个结果集来每一条记录来给model类的对象赋值,便可以产生一个model类对象的集合,则是我们想要的modelList。
【 有了这个工具之后的,那我们就可以肆无忌惮的变换参数,我换成nationModel.class我们便可以得到一系列籍贯,换成majorModel.class就可以得到一系列的专业,然后就是我上篇文章说的就可以采用这些list去初始化你界面的控件部分的内容了,但问题是你想要初始化的控件所需要的内容(内容就是model类的对象的list)所对应的字段在你的表存在。】
听完这个思路你可能觉得简单,事实上其中有很多值得推敲,研究的地方,废话不多说,听我娓娓道来。

firstStep:

(第一步,我们完成Model类与表的对应,还有表中的字段,类中的成员,将表得信息,model类的信息还有表中的字段与model类中的成员对应的信息都保存在classAndTable类中。在后期将通过hashmap将model类和与它相关的classAndTable类的对象作为键值进行存储,以便之后进行访问。
因为我们实例化一个model类的对象的时候,是通过表中的每一条记录来实例化的)

classAndTable类 (随便起的名字,意思是类与表的对应,先确定主要的成员

public class classAndTable {
    private Class<?> klass;
    private String table;
    private List<PropertyColumn> fieldList;
}   	

PropertyColumn类(model类中的一个成员对应表中的一个字段)

public class PropertyColumn {
    private Field field;
    private String column;
}

解释说明:
这个classAndTable是一个可以让model类和表对应起来的一个类 。

  1. Class<?> klass 表示不同的类;
  2. String table 类对应数据库中的某一个表
  3. List < propertyColumn> fieldList 一个model类中有多个成员与表中的多个字段对应形成以一个list,一个字段对应一个成员就形成了一个propertyColumn。
  4. 这里的PropertyColumn中的对于这个filed的解释为,它是model类的中的一个成员,采用Field来修饰是因为我们并不知道每一个成员的类型,所以我们采用field来定义一是更合理,二是因为我们可以采用反射机制通过成员的名称来初始化PropertyColumn类中filed。

【小结】 上面这两个类的结合将model类和表互相联系的信息已经封装在classAndTable类中,因为界面上有不同的控件,所以也对应的不同的model类,那自然而然也会有不同的表,那我们就得事先通过 配置文件 的方式,产生多个classAndTable类的对象,我们可以对每一个classAndTable类的对象进行初始化,初始化的内容就是model类和它对应的表以及表中的字段和model类中的成员的list列表。初始化完成后就形成一个classAndTable类的对象的list。
然后将个list和对应的model类名称形成键值对存入hashmap中。以后便可以通过model类的名字类在hashmap中找到对应的model类的classAndTable的的信息。

 Map<String, ClassTable> classTableMap;//
 //string 就是对应的model类的名称
 //classAndTable 就是这个model类与它所对应的表的一系列的信息

SecondStep:( 包含xml解析,xml文件)

这一步我们需要实现初始化model类和表的对应的内容)方便以后我们直接可以构造sql语句
xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<mappings>   
     <mapping class="com.mec.mis.model.NativeModel" table="sys_native_info"    
         <column property="id" name="id"></column>  
         <column property="name" name="name"></column>
     </mapping>
     <mapping class="com.mec.mis.model.NationModel" table="sys_nation_info">
         <column property="id" name="id"></column>
        <column property="name" name="name"></column>      
      </mapping>
</mappings>

我们来通过解析这个配置文件中的每一个mapping的方式来将model类和表的对应关系存储起来,(一个mapping事实上就是一个classAndTAble类的内容),为了解释的更清楚,我采用画图的方式来说明存储的方式(以第一个mapping为例子)

xml解析文件(相当于将xml解析后将每一个mapping的内容化作一个classAndTable类的对象进行存储在hashmap中,以后便可以通过hashmap进行键值匹配(根据model类的名称找到对应的classAndTable的对象)

public class ClassTableFactory {
   private static final Map<String, ClassTable> classTableMap;
 
   static {
      classTableMap = new HashMap<>();
   }
    
   public ClassTableFactory() {  
   }
	  
   public void loadMapping(String path) {
   try {
       new XMLParser() {
       @Override
        public void dealElement(Element element, int index) {
             String className = element.getAttribute("class");
             String tableName = element.getAttribute("table");
             ClassAndTable ct = new ClassAndTable();
    
       try {
         Class<?> klass = Class.forName(className);
         ct.setKlass(klass);
         ct.setTable(tableName);
          new XMLParser() {
               @Override
               public void dealElement(Element element, int index) {
                   String id = element.getAttribute("id");
                   String property = element.getAttribute("property");
                   String column = element.getAttribute("name");
        
                   PropertyColumn pc = new PropertyColumn();
                try {
                      pc.setField(klass.getDeclaredField(property));
                      pc.setColumn(column);
         
                      if (id.length() > 0) {
                          ct.setId(pc);
                      }
         
                      ct.addProperty(pc);
                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    } catch (SecurityException e) {
                        e.printStackTrace();
                    }
                 }
            }.parse(element, "column");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        classTableMap.put(className, ct);
      }
    }.parse(XMLParser.loadXml(path), "mapping");
  } catch (SAXException e) {
     e.printStackTrace();
  } catch (IOException e) {
     e.printStackTrace();
  } 
 }
 
 public static ClassAndTable getClassTable(Class<?> klass) {
  return classTableMap.get(klass.getName());
 }
 
 public static ClassAndTable getClassTable(String className) {
  return classTableMap.get(className);
 }

xml解析工具

public abstract class XMLParser {
    private static DocumentBuilder db;
 
    public XMLParser() {
    }
 
    public static Document loadXml(String xmlPath) throws SAXException, IOException {
       InputStream is = XMLParser.class.getResourceAsStream(xmlPath);
       return loadXml(is);
    }
 
    static {
       try {
          db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
       } catch (ParserConfigurationException e) {
          e.printStackTrace();
       }
     }
 
    public static Document loadXml(InputStream is) throws SAXException, IOException {
        return db.parse(is);
    }
 
    public abstract void dealElement(Element element, int index);
 
    public void parse(Document document, String tagname) {
         NodeList nodeList = document.getElementsByTagName(tagname);
         for (int index = 0; index < nodeList.getLength(); index++) {
            Element element = (Element) nodeList.item(index);
            dealElement(element, index);
          }
     }
 
     public void parse(Element element, String tagname) {
          NodeList nodeList = element.getElementsByTagName(tagname);
          for (int index = 0; index < nodeList.getLength(); index++) {
             Element ele = (Element) nodeList.item(index);
             dealElement(ele, index);
          }
      }
 
}

这两步操作已经将完成了model类与对应表信息的存储,存储在hashMap中,相当于map中的键就是某一个model类的名称,值就是与model类对应的表及其字段信息,为之后可以通过classTableFactory类中的方法getClassTable(Class<?> klass),通过将model类作为参数可以获取到对应的键值classAndTable类的一个对象,并且根据这个对象的内容进行构造sql语句,进行从数据库获取数据。

ThirdStep:(反射机制的应用)

classAndTable类方法的完善和propertyColumn类方法的完善。

propertyColumn类

在这里插入代码片
public class PropertyColumn {    
      private Field field;
      private String column;
 
      public PropertyColumn() {
      }
      public void setProperty(ResultSet resultSet, Object object) {
         try {
                 // 从表的当前一条记录中,获取以column的值为字段名的字段的值
             Object value = resultSet.getObject(column);
                 // 设置field(成员)的可访问性为真,即,private成员都可以被访问
             field.setAccessible(true);
                 // 更改对象object的field成员的值为value
             field.set(object, value);
          } catch (SQLException e) {
             e.printStackTrace();
          } catch (IllegalArgumentException e) {
             e.printStackTrace();
          } catch (IllegalAccessException e) {
             e.printStackTrace();
          }
       }
       //后边还有成员的getter和setter的程序就不在此多说了

classAndTable类

public class ClassAndTable {
    private Class<?> klass;
    private String table;
    private List<PropertyColumn> fieldList;
    private PropertyColumn id;
 
    public ClassTable() {
      fieldList = new ArrayList<>();
    }
    public void setFieldFromResultSet(ResultSet resultSet, Object object) {
      for (PropertyColumn field : fieldList) {
          field.setProperty(resultSet, object);
      }
    }
 
    public void addProperty(PropertyColumn property) {
      fieldList.add(property);
    }
 
    public String getIdString() {
      return table + "." + id.getColumn();
    }
 
    public String getColumnString() {
      StringBuffer res = new StringBuffer();
  
      boolean first = true;
      for (PropertyColumn field : fieldList) {
         res.append(first ? "" : ", ")
            .append(table).append('.')
            .append(field.getColumn());
   
         first = false;
       }
  
      return res.toString();
     }
    //后边的成员getter和setter方法不再说明
}

上述的两段代码,采用了反射机制。

  1. 因为classAndTable类主要是存储model类所对应的表的数据和字段与model类中的成员的一一对应关系,那自然可以采用这些信息构造出来sql语句主要部分,例如从哪个表获取数据,获取表中哪个字段的数据。
  2. propertyColumn类中有一个重要的方法就是setProperty(ResultSet resultSet, Object object) ,因为propertyColumn类的对象组成一个list做为classAndtable的一个成员。

我们本身的目的就是根据已知的model类,获知其对应的表,产生一个sql语句从表中获取需要的字段的信息,采用这些信息反过来初始化model类的对象,并形成一个model类对象的list去初始化控件。

所以看下面这段代码,采用 注释解释propertyColumn类中的setProperty( ) 方法和classAndTable类中的setFieldFromResultSet()方法

database类中的一个方法

public <T> List<T> list(Class<?> klass) {
        //事实上这个方法就是我们上篇文章所说的优化部分,把很多相同的代码归纳写为一个工具,只需要给不同的model类就可以。

     ClassAndTable ct = ClassTableFactory.getClassTable(klass);
        //在进行调用这个方法的时候,只需给出参数model类,这一句代码的意思就是根据这个model类,可以在 ClassTableFactory中的haspMap中根据键值对的得到model类对应的一个classAndTable类的对象,这个对象里边包含的是与这个model有关的表和字段相关信息。
  
     if (ct == null) {
        return null;
     }
     String sql = "SELECT " + ct.getColumnString() + " FROM " + ct.getTable();
       
        //这条语句是产生sql语句。
        // ct.getColumnString()是因为ClassAndTable这个类中有这个方法,将classAndtable中的表信息与字段信息组成一个字符串.
        // ct.getTable()也是classAndTable类中的一个方法,返回表名称字符串。
        // 这条sql语句说明了从哪个表中获取这个表中的哪些字段的信息。
 
     List<T> result = new ArrayList<>();
     try {
        PreparedStatement state = getConnection().prepareStatement(sql);
        ResultSet rs = state.executeQuery();
        //rs是一个结果集,就是通过sql语句从数据库获取到的字段信息。相当于多条记录的一个列表。
   
        while(rs.next()) {
           Object object = klass.newInstance();
            // 产生一个model类的对象.
           ct.setFieldFromResultSet(rs, object);

       //就是根据这个结果集对产生的每一个model类的对象进行赋值操作,此时下面的赋值操作就是用到了反射机制。
       // 之前说过一个表对应一个类,而表中的一条记录对应一个这个类的对象。
       // 此时的rs表示类结果集的第一条记录
       //ct.setFieldFromResultSet(rs, object)这个方法是classAndFactory中的,因为要通过一条记录对model类的对象进行赋值.
       //若一条记录汇总包含两个不同的字段编号和名称,那classAndFactory中有一个List<PropertyColumn> fieldList,那么这个list有两个PropertyColumn元素,一个元素中是编号成员和编号字段对应,另一个元素是名称成员和名称字段对应。
       //setFieldFromResultSet(rs, object)方法循环classAndFactory中的List<PropertyColumn> ,
       //setProperty(ResultSet resultSet, Object object)方法对利用List<PropertyColumn> 每一个元素中的成员和字段关系进行对model类的一个对象的每个成员进行赋值。
        
           result.add((T) object
         }
      } catch (SQLException e) {
          e.printStackTrace();
      } 
      
      return result;
 }

小结:

上边函数中while循环中的三步操作可以根据 classAndFactory类 中的 setFieldFromResultSet() 方法和 propertyColumn类 中的 setProperty( ) 方法进行结合分析。这个函数用来获取对应的modelList,也正是上一篇文章中所要优化部分的代码,在这里只需这一个函数便可以获取不同的modelList,只需在需要产生不同种类的modelList的函数中去调用上边这个函数即可。

那么在这篇文章开头的代码优化结果便是如下了,优化过程虽复杂,但确实做成工具非常的实用。
现在只需要根据传入不同的类就可以获得modelList,再也不需要重复的写类似的代码了。

public List<NativeModel> getNatives() {
    return database.list(NativeModel.class);
}

ps: 开开开山怪【原创文章】

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值