2015-06-18 94 views
0

我最近在我的web應用程序中編寫了一個類,用於解析一個巨大的XML文件並向其中添加一個數據庫表。我的應用程序在Wildfly9上運行,並使用JPA和Hibernate提供程序來處理MySQL數據庫。@資源數據源破壞數據庫連接

的AS配置是非常標準的,我只是說我的數據源的conf:

<datasource jta="false" jndi-name="java:jboss/datasources/spazio_visione" pool-name="spazio_visione" enabled="true" use-ccm="false"> 
        <connection-url>jdbc:mysql://127.0.0.1:3306/spazio_visione?zeroDateTimeBehavior=convertToNull&amp;rewriteBatchedStatements=true</connection-url> 
        <driver-class>com.mysql.jdbc.Driver</driver-class> 
        <driver>mysql</driver> 
        <security> 
         <user-name>myuser</user-name> 
         <password>mypasswd</password> 
        </security> 
        <validation> 
         <validate-on-match>false</validate-on-match> 
         <background-validation>false</background-validation> 
        </validation> 
        <statement> 
         <share-prepared-statements>false</share-prepared-statements> 
        </statement> 
       </datasource> 

而且這裏是我的persistence.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> 

    <persistence-unit name="backoffice" transaction-type="JTA"> 
     <jta-data-source>java:jboss/datasources/spazio_visione</jta-data-source>   
     <exclude-unlisted-classes>false</exclude-unlisted-classes> 
     <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> 
     <properties>    
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> 
      <property name="hibernate.cache.use_query_cache" value="true" /> 
      <property name="hibernate.cache.use_second_level_cache" value="true" /> 
      <property name="hibernate.jdbc.batch_size" value="100" /> 
      <property name="hibernate.order_inserts" value="true" /> 
      <property name="hibernate.order_updates" value="true" />    
      <!-- <property name="hibernate.show_sql" value="true"/> -->  
      <!-- <property name="hibernate.hbm2ddl.auto" value="validate"/> --> 
     </properties> 
    </persistence-unit> 

</persistence> 

一切一直工作正常,使用JPA實體管理我的領域模型。

回到我的解析器......實際上,由於很多原因,它需要使用本機JDBC查詢將數據插入到數據庫中。下面的代碼:

public class XMLFeedParser extends DefaultHandler { 

    @Inject Logger logger; 
    @Resource(lookup="java:jboss/datasources/spazio_visione") DataSource datasource; 

    private static final int STATEMENT_BATCH_THRESHOLD = 1000;  
    private MyXMLFeedItem item; 

    private Connection connection; 
    private PreparedStatement ps; 

    public XMLFeedParser() { 

    } 

    protected void initParser() throws SQLException { 

     connection = datasource.getConnection();  

     Statement deleteStatement = connection.createStatement(); 
     deleteStatement.executeUpdate("DELETE FROM mytable WHERE id_feed = "+feed.getId()); 
     deleteStatement.close(); 

     ps = connection.prepareStatement(
       "INSERT INTO mytable " 
       + "(first, second, ...) " 
       + "values (?, ?, ...)" 
       ); 
    } 

    protected void finalizeParser() throws SQLException { 
     if (ps!=null) { 
      ps.executeBatch(); 
      ps.close(); 
     } 
     if (connection!=null) { 
      connection.close(); 
     }  
    } 

    public void parseAndWriteToDatabase(String filePath) throws ParserConfigurationException, SAXException, IOException, SQLException { 

     File file = Paths.get(filePath).toFile();  

     SAXParserFactory factory = SAXParserFactory.newInstance();   
     SAXParser saxParser = factory.newSAXParser(); 

     initParser(); 
     saxParser.parse(file, this); 
     finalizeParser();    
    } 

    private void writeToDb(MyXMLFeedItem item) { 

     try { 

      ps.setString(1, "first"); 
      ps.setString(2, "second"); 
      // ... 
      ps.addBatch(); 

      if (counter % STATEMENT_BATCH_THRESHOLD == 0) { 
       ps.executeBatch(); 
      } 

     } catch (SQLException e) {    
      logger.severe(e.getMessage()); 
     } 

    } 

    @Override 
    public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attrs) throws SAXException {  
     // ...parsing logic 
    } 

    @Override 
    public void characters(char[] ch, int start, int length) throws SAXException { 
     // ...parsing logic 
    } 

    @Override 
    public void endElement(String namespaceURI, String localName, String qualifiedName) throws SAXException { 
     // calls writeToDb() for each record found 
    } 

} 

我XMLFeedParser在我的EJB之一,它將調用parseAndWriteToDatabase()方法注入(使用@Inject)。有用!

疼痛從這裏開始。自解析結束以來,我的應用程序開始在其他點隨機發出錯誤。堆棧跟蹤看起來是這樣的:

Caused by: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/spazio_visione 
    at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:646) 
    at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:552) 
    at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:737) 
    at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:138) 
    ... 165 more 
Caused by: javax.resource.ResourceException: IJ000655: No managed connections available within configured blocking timeout (30000 [ms]) 
    at org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreArrayListManagedConnectionPool.getConnection(SemaphoreArrayListManagedConnectionPool.java:553) 
    at org.jboss.jca.core.connectionmanager.pool.AbstractPool.getSimpleConnection(AbstractPool.java:622) 
    at org.jboss.jca.core.connectionmanager.pool.AbstractPool.getConnection(AbstractPool.java:594) 
    at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:579) 
    ... 168 more 

它看起來像我沒有關閉連接,但事實並非如此! 有什麼建議嗎?

回答

1

有幾種可能性會出現什麼問題。首先,您打開initParser()中的連接,但在finalizeParser()中關閉它,而不是最終使用。如果引發異常,則連接未關閉。使用try-with-resources仍然會更好。

另一個潛在的問題是該類不是線程安全的。例如,如果一個實例沒有同步使用,如果你finalizeParser()之前調用XMLFeedParser.initParser()兩次,你可能會失去你的參考connection,然後永遠不會關閉(請問你的EJB中注入了XMLFeedParser的樣子?)

編輯:使用try-with-resources: 它取決於你需要的地方Connection。您可以打開parseAndWriteToDatabase()中的連接並將其傳遞給您所需的方法。所以你不必明確地呼叫close()。另外你的PreparedStatements和ResultSets可以用try-with-resources打包。

例如像:

public void parseAndWriteToDatabase(String filePath) throws ParserConfigurationException, SAXException, IOException, SQLException { 

    // ... 
    try (Connection connection = getDataSource().getConnection();) 
    { 
    initParser(connection); 
    saxParser.parse(file, this); 
    finalizeParser(connection);    
    } 
} 

因此,當你Connection和其他變量是不是類的成員,你不必擔心其它線程訪問它們。

+0

其實,你的回答讓我以正確的方式! 我沒有檢查解析器中的異常,並且並不總是調用finalize。 您可以使用資源示例代碼嘗試豐富答案嗎? ty –

+1

增加了嘗試資源的示例 – user140547