本人目前負責的一個項目中,要用到將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; }
歡迎你們一塊兒討論。