最近,我在Web应用程序中编写了一个类,用于解析巨大的XML文件并将其内容提供给db表。我的应用程序在Wildfly9上运行,并与Hibernate提供程序一起使用JPA来处理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查询将我的数据插入db中。这是代码:

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被注入(使用@Inject)到我的一个EJB中,这将调用parseAndWriteToDatabase()方法。有用!

痛苦从这里开始。自解析结束以来,我的应用程序开始随机地在其他方面给出错误。 stacktrace看起来像这样:

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


好像我没有关闭连接,但这不是事实!
有什么建议么?

最佳答案

有几种可能出问题的地方。首先,您要在initParser()中打开连接,而在finalizeParser()中将其关闭,而无需最终使用。如果引发异常,则不会关闭连接。使用try-with-resources还是更好。

另一个潜在的问题是该类不是线程安全的。例如,如果使用一个没有同步的实例,如果您在XMLFeedParser.initParser()之前两次调用finalizeParser(),则可能会丢失对connection的引用,而该引用却永远不会关闭(注入XMLFeedParser的EJB外观如何)喜欢?)

编辑:使用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和其他变量不是该类的成员时,您不必担心其他线程会访问它们。

10-04 14:32
查看更多