jpa 如何在Hibernate中将查询超时设置为小于1秒?

lb3vh1jj  于 2023-11-18  发布在  其他
关注(0)|答案(5)|浏览(121)

我使用Hibernate 5.4.1.Final和Java 8。我通过以下方式将查询超时设置为TypedQuery

typedQuery.setHint("javax.persistence.query.timeout", 400);

字符串
但hib会将其转换为秒数并向下舍入为0。

typedQuery.getHints();
{org.hibernate.timeout=0, javax.persistence.query.timeout=0}


如果我把某个值设置为500以上,它会舍入到1秒。

typedQuery.setHint("javax.persistence.query.timeout", 500);
typedQuery.getHints();
{org.hibernate.timeout=1, javax.persistence.query.timeout=1000}


如何将查询超时设置为毫秒且小于1秒?
谢谢

yptwkmov

yptwkmov1#

Hibernate本身并不处理这个超时,而是通过JDBC Statement. setvalid方法将它提供给JDBC驱动程序。(参见:https://thoughts-on-java.org/11-jpa-hibernate-query-hints-every-developer-know/
请参阅此问题https://bugs.eclipse.org/bugs/show_bug.cgi?id=456067
当JDBC驱动程序只提供指定秒数的配置时,Hibernate可能会被迫将此值转换为秒数。请检查JDBC驱动程序提供的超时精度。这可能是预期的行为。
如果以秒为单位指定网络超时似乎更合适,因为很难避免随机的网络延迟。

nnvyjq4y

nnvyjq4y2#

我们通过设置PostgreSQL DB的statement_timeout参数找到了一个解决方法。当我们需要限制查询执行时间时,我们执行以下脚本。当然超时值是可配置的。运行查询后我们将此参数设置为0。

Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {

    @Override
    public void execute(Connection connection) throws SQLException {
        connection.createStatement().execute("set statement_timeout = 300");
    }
});

字符串

w8ntj3qf

w8ntj3qf3#

在调试了**(很多)Hibernate和我的(maria-db)JDBC连接器之后,我也遇到了同样的问题。
我注意到
下面的**代码行(在hibernate-core中),它基本上迫使您使用MS格式的秒(例如1000)来提示'javax.persistence.query.timeout',否则,它将舍入值(value是提示的值)
这基本上意味着你不能有1-999毫秒,而是0或1秒

int timeout = (int)Math.round( ConfigurationHelper.getInteger( value ).doubleValue() / 1000.0 );

字符串
代码行取自此源代码
我希望你能找到一个解决方案,因为我找不到一个变通办法

编辑:

唯一的办法我可以拉这关闭是使用连接的网络超时。
请注意,由于这是**“手动”完成的,因此不会像Hibernate那样为您进行清理(关闭/释放资源),也不会调用KILL命令,因此您必须自己**进行清理

int timeoutMs = 250;
var connection = jdbcTemplate.getDataSource().getConnection();
connection.setNetworkTimeout(Runnable::run, timeoutMs);
var callableStatement = connection.prepareCall("{call testSp()}");
// Setup your SP parameters
callableStatement.execute();

o7jaxewo

o7jaxewo4#

要为Hibernate管理的查询设置毫秒级的超时,您必须获得对Hibernate管理的JDBC连接的完全控制(而且,不,您不会被普通JDBC而不是Hibernate锁定)。
一种方法是实现你自己的ConnectionProvider。像往常一样,你想子类化你当前的ConnectionProvider,假设它是股票DatasourceConnectionProviderImpl,只覆盖两个方法:getConnection()closeConnection(Connection)

public class MillisConnectionProvider extends DatasourceConnectionProviderImpl {

    private final ConcurrentMap<Connection, Integer> alteredConnections = new ConcurrentHashMap<>();    

    @Override
    public Connection getConnection() throws SQLException {
        final Connection connection = super.getConnection();
        alteredConnections.put(connection, connection.getNetworkTimeout());
        connection.setNetworkTimeout(Runnable :: run, myTimeoutInMilliseconds);
        return connection;
    }
    
    @Override
    public void closeConnection(Connection connection) throws SQLException {
        connection.setNetworkTimeout(Runnable :: run, alteredConnections.remove(connection));
        super.closeConnection(connection);
    }
    
}

字符串
由于Connection在概念上是无状态的,因此应该并且将被重用,因此有必要将其初始超时存储在ConcurrentMap中,然后在Connection关闭时恢复超时。选择用于超时设置的Executor是一个普通的调用者,我们不需要任何更复杂的东西。
多线程问题:alteredConnections是线程安全的,因为ConnectionProvider当然是由应用程序中的多个线程同时访问的,但是没有更多的保护是必要的,因为Connection不是由Hibernate在线程之间共享的。
接下来,我们必须在Hibernate中注册我们的自定义ConnectionProvider。从5.3.9版开始,可以使用适当的ServiceContributor实现来完成:

public class MillisConnectionProviderServiceContributor implements ServiceContributor {
    
    @Override
    public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
        serviceRegistryBuilder.addService(ConnectionProvider.class, new MillisConnectionProviderServiceContributor());
    }

}


,那么它应该在META-INF/services/org.hibernate.integrator.spi.Integrator classpath资源中注册:

com.foo.MillisConnectionProviderServiceContributor


另一个很好的问题是如何定义myTimeoutInMilliseconds,因为它很可能已经被设置为每个Hibernate查询,Criteria等。这个问题没有简单的解决方案,因为在ConnectionProvider级别无法访问这些示例。我能想到的唯一简单的方法是使用ThreadLocal:将myTimeoutInMilliseconds值注入到线程存储中,并在ConnectionProvider级别检索它(不要忘记在关闭Connection时清除ThreadLocal的状态)。

xxe27gdn

xxe27gdn5#

试试这个:

typedQuery.setHint("org.hibernate.timeout", "1");
typedQuery.getSingleResult();

字符串

相关问题