简体   繁体   English

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

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

I am doing some big queries on my database with Hibernate and I sometimes hit timeouts. 我正在使用Hibernate对数据库进行一些大查询,有时会遇到超时问题。 I would like to avoid setting the timeout manually on every Query or Criteria . 我想避免在每个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查询上设置默认超时值?

JPA 2 defines the javax.persistence.query.timeout hint to specify default timeout in milliseconds. JPA 2定义了javax.persistence.query.timeout提示以指定默认超时(以毫秒为单位)。 Hibernate 3.5 (currently still in beta) will support this hint. Hibernate 3.5(目前仍处于beta版)将支持此提示。

See also https://hibernate.atlassian.net/browse/HHH-4662 另请参阅https://hibernate.atlassian.net/browse/HHH-4662

JDBC has this mechanism named Query Timeout, you can invoke setQueryTime method of java.sql.Statement object to enable this setting. JDBC具有一种称为查询超时的机制,您可以调用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,以透明地为每个语句设置Query Timeout!

Spring config file: Spring配置文件:

<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>

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 在返回Query对象之前,使用工厂或基类方法创建所有查询并设置超时
  • Create your own version of org.hibernate.loader.Loader and set the timeout in doQuery 创建您自己的org.hibernate.loader.Loader版本并在doQuery中设置超时
  • Use AOP, eg Spring, to return a proxy for Session; 使用AOP(例如Spring)返回Session的代理; add advice to it that wraps the createQuery method and sets the timeout on the Query object before returning it 向其添加包装createQuery方法的建议,并在返回之前在Query对象上设置超时

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. 要在事务(INSERT / UPDATE)级别设置全局超时值-将以下内容添加到配置文件中。

<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>

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查询设置超时间隔的更多详细信息,请参阅本文

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM