Как установить время ожидания запроса по умолчанию в JPA и Hibernate?

Я делаю несколько больших запросов в своей базе данных с помощью Hibernate, и иногда у меня возникают тайм-ауты. Я бы не хотел устанавливать время ожидания вручную для каждого Query или Criteria.

Есть ли какое-либо свойство, которое я могу дать моей конфигурации Hibernate, которая установила бы приемлемое значение по умолчанию для всех запросов, которые я запускаю?

Если нет, как я могу установить значение времени ожидания по умолчанию для запросов Hibernate?

Ответ 1

JPA 2 определяет подсказку javax.persistence.query.timeout для указания времени ожидания по умолчанию в миллисекундах. Hibernate 3.5 (в настоящее время все еще в бета-версии) будет поддерживать эту подсказку.

Смотрите также https://hibernate.atlassian.net/browse/HHH-4662

Ответ 2

JDBC имеет этот механизм с именем Query Timeout, вы можете вызвать метод setQueryTime объекта java.sql.Statement, чтобы включить этот параметр.

Спящий режим не может сделать это унифицированным способом.

Если ваше приложение извлекает соединение JDBC vi java.sql.DataSource, вопрос может быть легко разрешен.

мы можем создать DateSourceWrapper для соединения с прокси-сервером, которые выполняют setQueryTimeout для каждого созданного ею оператора.

Пример кода легко читается, я использую некоторые классы 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;
}

}

Теперь мы можем использовать этот QueryTimeoutConfiguredDataSource, чтобы обернуть существующий DataSource, чтобы установить Query Timeout для каждого отчета прозрачно!

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>

Ответ 3

Вот несколько способов:

  • Используйте метод factory или базового класса для создания всех запросов и установки таймаута перед возвратом объекта Query
  • Создайте свою собственную версию org.hibernate.loader.Loader и установите тайм-аут в doQuery
  • Использовать АОП, например. Spring, чтобы вернуть прокси для сеанса; добавьте в него совет, который обертывает метод createQuery и устанавливает тайм-аут объекта Query перед его возвратом.

Ответ 4

Для установки глобальных значений таймаута на уровне запроса - добавьте ниже в файл конфигурации.

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

Для установки значений глобального тайм-аута на уровне транзакции (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>

Ответ 5

Да, вы можете сделать это.

Как я объяснил в этой статье, все, что вам нужно сделать, это передать подсказку запроса JPA как глобальное свойство:

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

Теперь при выполнении запроса JPQL, который истечет через 1 секунду:

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 выдаст исключение тайм-аута запроса:

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

Дополнительные сведения об установке интервала ожидания для запросов Hibernate см. В этой статье.