【问题标题】:@Resource datasource breaking DB connection@Resource 数据源断开数据库连接
【发布时间】:2015-06-18 08:16:56
【问题描述】:

我最近在我的 Web 应用程序中编写了一个类,用于解析一个巨大的 XML 文件并将其内容提供给一个 db 表。我的应用程序在 Wildfly9 上运行,并使用 JPA 和 Hibernate 提供程序来处理 MySQL 数据库。

AS 配置很标准,我刚刚添加了我的数据源配置:

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

痛苦从这里开始。自解析结束以来,我的应用程序开始在其他点出现错误,只是随机的。堆栈跟踪如下所示:

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

看起来我没有关闭连接,但那不是真的! 有什么建议吗?

【问题讨论】:

    标签: java jpa jdbc jboss wildfly


    【解决方案1】:

    有几种可能会出错。首先,您在initParser() 中打开连接,但在finalizeParser() 中关闭它,而不使用finally。如果抛出异常,则连接不会关闭。使用 try-with-resources 会更好。

    另一个潜在的问题是该类不是线程安全的。例如,如果一个实例在没有同步的情况下使用,如果您在finalizeParser() 之前调用XMLFeedParser.initParser() 两次,您可能会丢失对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 和其他变量不是该类的成员时,您不必担心其他线程访问它们。

    【讨论】:

    • 其实,你的回答让我走对了!我没有检查解析器的异常,并且并不总是调用 finalize。您能否通过尝试资源示例代码来丰富答案? ty
    • 添加了 try-with-resources 示例
    猜你喜欢
    • 2019-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-18
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多