Java 如何使用 JPA 和 Hibernate 设置默认查询超时?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2101455/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:33:03  来源:igfitidea点击:

How to set a default query timeout with JPA and Hibernate?

javahibernatejpaconfigurationtimeout

提问by malaverdiere

I am doing some big queries on my database with Hibernate and I sometimes hit timeouts. I would like to avoid setting the timeout manually on every Queryor Criteria.

我正在使用 Hibernate 对我的数据库进行一些大查询,有时我会遇到超时。我想避免在每个QueryCriteria.

Is there any property I can give to my Hibernate configuration that would set an acceptable default for all queries I run?

我可以为我的 Hibernate 配置提供任何属性来为我运行的所有查询设置可接受的默认值吗?

If not, how can I set a default timeout value on Hibernate queries?

如果没有,如何在 Hibernate 查询上设置默认超时值?

采纳答案by Hardy

JPA 2 defines the javax.persistence.query.timeouthint to specify default timeout in milliseconds. Hibernate 3.5 (currently still in beta) will support this hint.

JPA 2 定义了javax.persistence.query.timeout提示以指定默认超时(以毫秒为单位)。Hibernate 3.5(目前仍处于测试阶段)将支持此提示。

See also https://hibernate.atlassian.net/browse/HHH-4662

另见https://hibernate.atlassian.net/browse/HHH-4662

回答by Brian Deterling

Here are a few ways:

这里有几个方法:

  • Use a factory or base class method to create all queries and set the timeout before returning the Query object
  • Create your own version of org.hibernate.loader.Loader and set the timeout in doQuery
  • Use AOP, e.g. Spring, to return a proxy for Session; add advice to it that wraps the createQuery method and sets the timeout on the Query object before returning it
  • 使用工厂或基类方法创建所有查询并在返回 Query 对象之前设置超时
  • 创建自己的 org.hibernate.loader.Loader 版本并在 doQuery 中设置超时
  • 使用AOP,例如Spring,为Session返回一个代理;向它添加建议,包装 createQuery 方法并在返回它之前在 Query 对象上设置超时

回答by Rehtron

JDBC has this mechanism named Query Timeout, you can invoke setQueryTime method of java.sql.Statement object to enable this setting.

JDBC 有这种名为 Query Timeout 的机制,您可以调用 java.sql.Statement 对象的 setQueryTime 方法来启用此设置。

Hibernate cannot do this in unified way.

Hibernate 不能以统一的方式做到这一点。

If your application retrive JDBC connection vi java.sql.DataSource, the question can be resolved easily.

如果您的应用程序通过 java.sql.DataSource 检索 JDBC 连接,则问题很容易解决。

we can create a DateSourceWrapper to proxy Connnection which do setQueryTimeout for every Statement it created.

我们可以创建一个 DateSourceWrapper 来代理 Connnection,它为它创建的每个 Statement 执行 setQueryTimeout。

The example code is easy to read, I use some spring util classes to help this.

示例代码很容易阅读,我使用了一些 spring util 类来帮助它。

public class QueryTimeoutConfiguredDataSource extends DelegatingDataSource {

private int queryTimeout;

public QueryTimeoutConfiguredDataSource(DataSource dataSource) {
    super(dataSource);
}

// override this method to proxy created connection
@Override
public Connection getConnection() throws SQLException {
    return proxyWithQueryTimeout(super.getConnection());
}

// override this method to proxy created connection
@Override
public Connection getConnection(String username, String password) throws SQLException {
    return proxyWithQueryTimeout(super.getConnection(username, password));
}

private Connection proxyWithQueryTimeout(final Connection connection) {
    return proxy(connection, new InvocationHandler() {
        //All the Statement instances are created here, we can do something
        //If the return is instance of Statement object, we set query timeout to it
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object object = method.invoke(connection, args);
            if (object instanceof Statement) {
                ((Statement) object).setQueryTimeout(queryTimeout);
            }
            return object;
        });
}

private Connection proxy(Connection connection, InvocationHandler invocationHandler) {
    return (Connection) Proxy.newProxyInstance(
            connection.getClass().getClassLoader(), 
            ClassUtils.getAllInterfaces(connection), 
            invocationHandler);
}

public void setQueryTimeout(int queryTimeout) {
    this.queryTimeout = queryTimeout;
}

}

}

Now we can use this QueryTimeoutConfiguredDataSource to wrapper your exists DataSource to set Query Timeout for every Statement transparently!

现在我们可以使用这个 QueryTimeoutConfiguredDataSource 来包装您现有的 DataSource 以透明地为每个 Statement 设置查询超时!

Spring config file:

弹簧配置文件:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource">
        <bean class="com.stackoverflow.QueryTimeoutConfiguredDataSource">
            <constructor-arg ref="dataSource"/>
            <property name="queryTimeout" value="1" />
        </bean>
    </property>
</bean>

回答by spotu

For setting global timeout values at query level - Add the below to config file.

要在查询级别设置全局超时值 - 将以下内容添加到配置文件中。

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
    <property name="queryTimeout" value="60"></property>
</bean>

For setting global timeout values at transaction(INSERT/UPDATE) level - Add the below to config file.

要在事务(插入/更新)级别设置全局超时值 - 将以下内容添加到配置文件中。

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="myEmf" />
    <property name="dataSource" ref="dataSource" />
    <property name="defaultTimeout" value="60" />
    <property name="jpaDialect">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
    </property>
</bean>

回答by Vlad Mihalcea

Yes, you can do that.

是的,你可以这样做。

As I explained in this article, all you need to do is to pass the JPA query hint as a global property:

正如我在本文中所解释的,您需要做的就是将 JPA 查询提示作为全局属性传递:

<property
    name="javax.persistence.query.timeout"
    value="1000"
/>

Now, when executing a JPQL query that will timeout after 1 second:

现在,当执行 1 秒后超时的 JPQL 查询时:

List<Post> posts = entityManager
.createQuery(
    "select p " +
    "from Post p " +
    "where function('1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(2) ) --',) is ''", Post.class)
.getResultList();

Hibernate will throw a query timeout exception:

Hibernate 会抛出查询超时异常:

SELECT p.id AS id1_0_,
       p.title AS title2_0_
FROM post p
WHERE 1 >= ALL (
    SELECT 1
    FROM pg_locks, pg_sleep(2)
) --()=''

-- SQL Error: 0, SQLState: 57014
-- ERROR: canceling statement due to user request

For more details about setting a timeout interval for Hibernate queries, check out this article.

有关为 Hibernate 查询设置超时间隔的更多详细信息,请查看这篇文章