本人目前负责的一个项目中,要用到将pdm文件导入到MySQL的功能。最近遇到麻烦了,因为程序是部署在阿里云上(内存只有2G), 且pdm文件比较大(约10M+),每次解析pdm文件10秒钟,就会致使tomcat自动关闭,搞的咱们很尴尬。常规的办法:html
解析节点的公共方法java
public Object evaluate(String path, Object object, QName returnType) { try { return XPathFactory.newInstance().newXPath().evaluate(path, object, returnType); } catch (XPathExpressionException e) { // 捕捉到异常不进行抛出操做,直接返回null,由后续方法进行判断处理。 return null; } }
解析表的方法数组
public void parseTable(Node modelNode, MetaDomainDTO domain) { NodeList tableList = (NodeList) evaluate(PATH_O_TABLE, modelNode, XPathConstants.NODESET); List<MetaEntityDTO> entityList = new ArrayList<MetaEntityDTO>(); for (int i = 0; i < tableList.getLength(); i++) { Node tableNode = tableList.item(i);// 获取table标签的解析对象 MetaEntityDTO table = new MetaEntityDTO();// 获取table实例 // 获取表id值 Long id = parseIdToLong(getAttrValue(TAG_ATTR_ID, tableNode)); // 获取<a:Name>标签内容做为描述信息 String name = getAttrContent(PATH_A_NAME, tableNode); // 获取<a:Code>标签内容做为名称 String code = getAttrContent(PATH_A_CODE, tableNode); // 获取<a:Comment>标签内容做为名称 String comment = getAttrContent(PATH_A_COMMENT, tableNode); table.setEntityId(id); table.setEntityName(name); table.setEntityCode(code.toLowerCase()); table.setEntityComment(comment); table.setDomainId(domain.getDomainId()); table.setVirtualId(id); // 解析字段内容 parseColumn(tableNode, table); // 解析键内容 parseKey(tableNode, table); tableCache.put(id, table); entityList.add(table); } domain.setEntityList(entityList); }
以上是目前代码中的核心方法。为了定位究竟是哪一步慢,我先减小pdm文件的大小,发现当文件是4M的时候解析须要15分钟,当文件是10M的时候,解析须要90分钟。我也观察了内存的使用状况,发现会常常发生GC。tomcat
pdm文件其实就是xml文件,我跳出了惯性思惟,想一想是否还有更加快的解析xml文件的方法呢?经过度娘,找到答案。框架
(1)DOM解析
DOM是html和xml的应用程序接口(API),以层次结构(相似于树型)来组织节点和信息片断,映射XML文档的结构,容许获取和操做文档的任意部分,是W3C的官方标准
【优势】
①容许应用程序对数据和结构作出更改。
②访问是双向的,能够在任什么时候候在树中上下导航,获取和操做任意部分的数据。
【缺点】
①一般须要加载整个XML文档来构造层次结构,消耗资源大。
【解析详解】
①构建Document对象:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = bdf.newDocumentBuilder();
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(xml文件);
Document doc = bd.parse(is);
②遍历DOM对象
Document: XML文档对象,由解析器获取
NodeList: 节点数组
Node: 节点(包括element、#text)
Element: 元素,可用于获取属性参数dom
(2)SAX(Simple API for XML)解析
流模型中的"推"模型分析方式。经过事件驱动,每发现一个节点就引起一个事件,事件推给事件处理器,经过回调方法完成解析工做,解析XML文档的逻辑须要应用程序完成
【优点】
①不须要等待全部数据都被处理,分析就能当即开始。
②只在读取数据时检查数据,不须要保存在内存中。
③能够在某个条件获得知足时中止解析,没必要解析整个文档。
④效率和性能较高,能解析大于系统内存的文档。
【缺点】
①须要应用程序本身负责TAG的处理逻辑(例如维护父/子关系等),文档越复杂程序就越复杂。
②单向导航,没法定位文档层次,很难同时访问同一文档的不一样部分数据,不支持XPath。
【原理】
简单的说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束时通知事件处理函数(回调函数),进行相应处理,直到文档结束
【事件处理器类型】
①访问XML DTD:DTDHandler
②低级访问解析错误:ErrorHandler
③访问文档内容:ContextHandler
【DefaultHandler类】
SAX事件处理程序的默认基类,实现了DTDHandler、ErrorHandler、ContextHandler和EntityResolver接口,一般作法是,继承该基类,重写须要的方法,如startDocument()
【建立SAX解析器】
SAXParserFactory saxf = SAXParserFactory.newInstance();
SAXParser sax = saxf.newSAXParser();
注:关于遍历
①深度优先遍历(Depthi-First Traserval)
②广度优先遍历(Width-First Traserval)函数
(3)JDOM(Java-based Document Object Model)
Java特定的文档对象模型。自身不包含解析器,使用SAX
【优势】
①使用具体类而不是接口,简化了DOM的API。
②大量使用了Java集合类,方便了Java开发人员。
【缺点】
①没有较好的灵活性。
②性能较差。性能
(4)DOM4J(Document Object Model for Java)
简单易用,采用Java集合框架,并彻底支持DOM、SAX和JAXP
【优势】
①大量使用了Java集合类,方便Java开发人员,同时提供一些提升性能的替代方法。
②支持XPath。
③有很好的性能。
【缺点】
①大量使用了接口,API较为复杂。优化
(5)StAX(Streaming API for XML)
流模型中的拉模型分析方式。提供基于指针和基于迭代器两种方式的支持,JDK1.6新特性
【和推式解析相比的优势】
①在拉式解析中,事件是由解析应用产生的,所以拉式解析中向客户端提供的是解析规则,而不是解析器。
②同推式解析相比,拉式解析的代码更简单,并且不用那么多库。
③拉式解析客户端可以一次读取多个XML文件。
④拉式解析容许你过滤XML文件和跳过解析事件。
【简介】
StAX API的实现是使用了Java Web服务开发(JWSDP)1.6,并结合了Sun Java流式XML分析器(SJSXP)-它位于javax.xml.stream包中。XMLStreamReader接口用于分析一个XML文档,而XMLStreamWriter接口用于生成一个XML文档。XMLEventReader负责使用一个对象事件迭代子分析XML事件-这与XMLStreamReader所使用的光标机制造成对照。ui
当文件很小的时候,用上面的多种方法,性能相差很少。可是当文件到达5M后,性能的差别就显示出来。
除了性能以外,还须要考虑具体的应用场景,选用不一样的方案。
(1)DOM解析的方案,所有调整成(4)DOM4J方案。一样是10M的pdm文件,解析只要20秒,不是一个数量级的。核心源码以下:
public List<Table> loadAllTables(Element element1){ List<Table> allTables = new ArrayList<Table>(); //外键 List<Reference> allReferences = loadAllReferences(element1); Element tableNode =(Element) element1.element("Tables"); //若是主题域下面没有表关联,则直接返回 if(tableNode==null){ return allTables; } for(Object t1:tableNode.elements("Table")){ Element t=(Element)t1; Table table = new Table(); List<Column> allColumns = new ArrayList<Column>(); String tableId = t.attributeValue("Id"); String tableName = t.elementText("Name"); String tableCode = t.elementText("Code"); String tableComment = t.elementText("Comment"); table.setTableId(tableId); table.setTableName(tableName); table.setTableCode(tableCode); table.setTableComment(tableComment); //主键 List<String> allPKIds = loadAllPKIds(t); //Column信息添加 Element columnNode = t.element("Columns"); for(Object col1:columnNode.elements()){ Element col=(Element)col1; Column column = new Column(); String columnId = col.attributeValue("Id"); String columnName = col.elementText("Name"); String columnCode = col.elementText("Code"); String columnComment = col.elementText("Comment"); String dataType = col.elementText("DataType"); Long length = NumberUtils.toLong(col.elementText("Length")); Long precision = NumberUtils.toLong(col.elementText("Precision")); // Long mandatory = NumberUtils.toLong(col.elementText("LogicalAttribute.Mandatory")); boolean pk = false; //获取主键 if(allPKIds.contains(columnId)){ pk = true; } boolean fk = false; //获取外键 for(Reference ref : allReferences){ if(columnId.equals(ref.getFKId())){ fk = true; table.setParentTableId(ref.getParentTableId()); } } column.setColId(columnId); column.setColName(columnName); column.setColCode(columnCode); column.setColComment(columnComment); column.setDataType(dataType); column.setLength(length); column.setPrecision(precision); column.setPK(pk); column.setFK(fk); allColumns.add(column); } table.setAllColumns(allColumns); allTables.add(table); } return allTables; }
欢迎你们一块儿讨论。