如何为Hibernate查询设置超时?

后端开发 2026-07-12

有一些运行时间较长的查询会消耗应用程序的内存。我尝试设置超时,在应用程序还没响应前中断执行,但我尝试的Hibernate和 Oracle都不尊重我设定的任何超时。如何强制查询超时?

Oracle数据库 - Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

Hibernate - 5.6.10

java - java 17

Spring 5.3.21

<bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@localhost:1521/ltoi" />
        <property name="username" value="" />
        <property name="password" value="" />               
</bean>
<bean id="hibernateProperties"
        class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <description>
            Globale Einstellungen/Properties zu Hibernate
            Connection Pool, Caching-Strategie, Fetch-Strategie, ...
      </description>
        <property name="properties">
            <props>
                <prop key="hibernate.dialect">de.cfs.hbn.CustomOracleDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.default_schema">CASHDATA</prop>
                <prop key="hibernate.current_session_context_class">thread</prop>
                <!--  <prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory</prop> -->
                <prop key="hibernate.transaction.coordinator_class">jdbc</prop>
                <prop key="hibernate.connection.release_mode">after_transaction</prop>
                <prop key="hibernate.max_fetch_depth">4</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">50</prop>
                <prop key="hibernate.order_inserts">true</prop>
                <prop key="hibernate.order_updates">true</prop>
                <prop key="hibernate.jdbc.batch_versioned_data">true</prop>               
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.cache.use_query_cache">false</prop>
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
                <prop key="hibernate.id.new_generator_mappings">true</prop>
                <prop key="hibernate.hql.bulk_id_strategy">org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy</prop>
                <prop key="hibernate.allow_update_outside_transaction">true</prop>

                <prop key="hibernate.use_sql_comments">true</prop>

            </props>
        </property>
    </bean>


@Test
    public void timeoutTest2()
    {
        Session se = sessionFactory.openSession();

        String query = """
                select c.id
                from Transaction c
                join Customer k on k.id = c.kd_id
                where k.kd_nr = 35
                """;

        List<BigDecimal> resultList = se.createNativeQuery(query)        
        //.setHint("org.hibernate.timeout", 1)
        .setTimeout(1)
        .getResultList();

        se.close();

        System.out.println(resultList.size());

        assertTrue(resultList.size() > 0);

    }
try
    {
        //Class.forName(OracleDriver.class.getClass().getName().toString()).newInstance();
        Connection conn = DriverManager
                .getConnection("jdbc:oracle:thin:@127.0.0.1:1521/LTOI", "user", "password");            

        conn.setAutoCommit(false);

        PreparedStatement statement = conn.prepareStatement("select * from cashdata.neu_cashpoint_xml");
        statement.setQueryTimeout(2);
        statement.setFetchSize(1000000);

        ResultSet result = statement.executeQuery();

        while (result.next())
        {                
            System.out.println(result.getBigDecimal(1));                
        }

        conn.commit();
        result.close();
        statement.close();
        conn.close();
    }
    catch (SQLException e)
    {
        System.out.println(e.getMessage());
    }

解决方案

Oracle数据库自8i版本及更早就支持查询超时。(实际上,Oracle通过从客户端发送信号来取消正在运行的查询。)

JDBC标准也支持这个功能(setQueryTimeout),但该特性是可选的,因为某些DBMS不支持它。因此你可能需要检查你当前的JDBC驱动版本,可能它不支持,或者实现有缺陷。

另外,在处理DML语句时,查询不会立即被取消,你必须等待事务/语句回滚。

更新:当Oracle JDBC驱动程序支持超时时,你应该在JVM中看到一个名为OracleTimeoutPollingThread(或类似名称)的线程。这个线程负责发送“取消消息”。

此外,取消正在运行的查询是通过TCP头中的OOB/URG标志实现的。你可以通过TCP dump捕获这条消息:

tcpdump -i <iface> host dbhost and port 1521 -vvv -n 'tcp[13] & 0x20 != 0'
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章