$filter 在带有 MySQL 的 JPA/Olingo 2.0.11 中不起作用

Posted

技术标签:

【中文标题】$filter 在带有 MySQL 的 JPA/Olingo 2.0.11 中不起作用【英文标题】:$filter not working in JPA/Olingo 2.0.11 with MySQL 【发布时间】:2020-09-19 04:42:56 【问题描述】:

Olingo2 2.0.11 版本与 spring-boot-starter-web 2.0.0 或更高版本有问题!

我基于此GitHub repository 使用olingo2jpaspring-boot 进行了odata 服务。

我已将项目设置为使用 MariaDB 数据库,它运行良好

不过项目有点老了,我尝试升级一下!

如果您在GitHub 中查看其pom.xml,您将看到以下详细信息:

...
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.2.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.7</java.version>
        <cxf.version>3.1.5</cxf.version>
        <olingo.version>2.0.6</olingo.version>
    </properties>
...

在第一步中,我尝试像这样更新库版本:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <cxf.version>3.3.6</cxf.version>
        <olingo.version>2.0.11</olingo.version>
    </properties>

完成此升级后,我需要更新 CxfServletRegister.javaApplication.java 文件中的两个导入,如下所示:

/* In file: odata-boilerplate/src/main/java/com/penninkhof/odata/utils/CxfServletRegister.java
*/
// import org.springframework.boot.context.embedded.ServletRegistrationBean; <- old class replace with
import org.springframework.boot.web.servlet.ServletRegistrationBean; // <- new address

/* In file: odata-boilerplate/src/main/java/com/penninkhof/odata/Application.java
*/
//import org.springframework.boot.context.web.SpringBootServletInitializer; <-- old class replace with
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; // <- new address

通过这些简单的更改,我可以使用命令mvn spring-boot:run -P jar 再次运行该应用程序。

但是,我在最新版本的spring boot 中理解为2.3.0.RELEASE,它会在每次请求到达时运行一个新的服务器实例。

直到这里一切似乎都很好。不过有个小问题!

虽然我在odata-boilerplate/src/main/resources/application.properties 文件中设置了dialect 值,如下所示:

# WEB SERVER 
server.port=9090

# MARIADB DATA SOURCE
spring.datasource.url = jdbc:mariadb://localhost:3306/cimply_ask?useUnicode=yes&characterEncoding=UTF-8
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.username = root
spring.datasource.password = 
spring.datasource.testWhileIdle = false
spring.datasource.validationQuery = SELECT 1

# JPA / HIBERNATE
spring.jpa.database-platform=org.hibernate.dialect.MariaDBDialect
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MariaDBDialect
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = update
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.hibernate.auto_quote_keyword=true

它只设置在负责初始化数据库初始值的线程(或任务)中!这是控制台输出的一部分。可以看出,日志属于线程task-1,并且在第 6 行中它为线程设置了正确的方言。

1- 22:22:29.075 [task-1] INFO  org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
2- 22:22:29.177 [task-1] INFO  org.hibernate.Version - HHH000412: Hibernate ORM core version 5.4.15.Final
3- 22:22:29.459 [task-1] INFO  org.hibernate.annotations.common.Version - HCANN000001: Hibernate Commons Annotations 5.1.0.Final
4- 22:22:29.715 [task-1] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
5- 22:22:29.817 [task-1] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
6- 22:22:29.840 [task-1] INFO  org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.MariaDBDialect
7- 22:22:31.067 [task-1] INFO  o.h.e.t.jta.platform.internal.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
8- 22:22:31.317 [task-1] INFO  o.s.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'

但是当请求到达时会发生什么?

例如,当我尝试以下请求时,我对字符串类型的列使用过滤:

http://localhost:9090/odata.svc/Members?$format=json&$filter=FirstName eq 'Jack'

我在邮递员或浏览器中收到一条错误消息以响应我的请求,如下所示:


    "error": 
        "code": null,
        "message": 
            "lang": "en",
            "value": "org.hibernate.exception.SQLGrammarException: could not extract ResultSet"
        
    

它实际上在数据库上生成了错误的查询,因为它没有设置正确的方言。这是我的应用程序中的控制台输出:

22:56:52.593 [http-nio-9090-exec-1] INFO  org.apache.cxf.endpoint.ServerImpl - Setting the server's publish address to be /
22:57:22.605 [http-nio-9090-exec-1] WARN  org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 1064, SQLState: 42000
22:57:22.606 [http-nio-9090-exec-1] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - You have an error in your SQL syntax; check the manual that corresponds
to your MariaDB server version for the right syntax to use near ''\'' at line 1

这里有什么问题?它在查询中使用escape '\' 而不是escape '\\',因为它没有根据数据库类型设置正确的方言!

我需要做什么?我需要知道如何为负责响应此应用中的请求的实例设置正确的方言?

请同时考虑以下几点:

    通过升级此应用程序以使用最新版本的spring-boot-starter,它会在每次收到新请求时创建一个新线程。您可以发送相同的请求,您会看到线程名称将更改为:[http-nio-9090-exec-2][http-nio-9090-exec-3][http-nio-9090-exec-1],...有时甚至使用旧线程!

    每次有新请求到达时,以下代码部分是处理请求的入口点。 也许这是我必须为线程设置方言的地方!

/* File: odata-boilerplate/src/main/java/com/penninkhof/odata/utils/JPAServiceFactory.java
*/

import javax.persistence.EntityManagerFactory;

import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext;
import org.apache.olingo.odata2.jpa.processor.api.ODataJPAServiceFactory;
import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPARuntimeException;

public class JPAServiceFactory extends ODataJPAServiceFactory 
    public static final String DEFAULT_ENTITY_UNIT_NAME = "Model";
    public static final String ENTITY_MANAGER_FACTORY_ID = "entityManagerFactory";

    @Override
    public ODataJPAContext initializeODataJPAContext() throws ODataJPARuntimeException 
        ODataJPAContext oDataJPAContext = getODataJPAContext();

        EntityManagerFactory factory = (EntityManagerFactory) SpringContextsUtil.getBean(ENTITY_MANAGER_FACTORY_ID);

        oDataJPAContext.setEntityManagerFactory(factory);
        oDataJPAContext.setPersistenceUnitName(DEFAULT_ENTITY_UNIT_NAME);
        oDataJPAContext.setJPAEdmExtension(new JPAEdmExtension());
        ODataContextUtil.setODataContext(oDataJPAContext.getODataContext());

        return oDataJPAContext;
    

这个问题有点长,但我试图解释我的所有调查和解决方法。我需要知道如何设置应用程序中的所有线程都使用的 默认方言!正如我之前提到的,我尝试在application.properties 文件中执行此操作,但它似乎在请求处理时会被忽略!

【问题讨论】:

相关post 可能有用:Link1、Link2、Link3 我提到了olingo 2.0.11连接mysql时的另一个问题here。 【参考方案1】:

不能解决原始问题,也不是最有效的方法.. 但这里有一个解决方法,可以解决如何只用一个斜杠删除不正确的“转义”语句:

public class SqlStatementInspector implements StatementInspector 

    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(SqlStatementInspector.class);

    @Override
    public String inspect(String sql) 
        if (!sql.contains("escape \'\\'")) 
            return sql;
        
        // OData JPA query correction -> current version (2.0.11) contains
        // the invalid 'escape "\"' statement that delivers no results
        LOG.info("Replacing invalid statement: escape \"\\\"");
        return sql.replace("escape \'\\'", "");
    

这会覆盖inspect方法,你可以在使用hibernate时修改生成的sql查询

然后我需要在我的 persistence.xml 文件中设置属性“hibernate.session_factory.statement_inspector”以将我的 StatementInspector 实现与我的休眠会话工厂连接起来

<property
                name="hibernate.session_factory.statement_inspector"
                value="SqlStatementInspector" />

我不知道这将如何与 spring-boot 一起工作,但也许您的 application.properties 有一个类似的属性?

【讨论】:

编辑了我的答案 - 我必须在我的 persistence.xml 文件中添加一个属性 谢谢,它也对我有用。我还需要编辑我的application.properties 以将新类传递给hibernate,如下所示:spring.jpa.properties.hibernate.session_factory.statement_inspector = me.cimply.ask.odata.utils.SqlStatementInspector 我不会接受这个答案作为最终答案。据我了解,问题出在 olingo 2.0.11 或 spring boot 2.3.0 中!其中之一阻止在主线程中设置与数据库相关的默认方言!如果我们将 olingo 降级到 2.0.10,我们可以在控制台中看到方言是在主线程中设置的,而当我们使用 olingo 2.0.11 时不会发生这种情况。 好吧..我不认为这是 Spring Boot 中的问题,因为我不使用它并且仍然不得不面对这个问题。而且因为我成功地尝试了 olingo 2.0.7(其他一切都没有改变).. 我会接受你的回答。因为我试图在我的线程中打印方言名称。令人惊讶的是,方言已经正确设置。我认为这是 onlingo 2.0.11 中的一个错误。不幸的是,他们在他们的 git 中关闭了问题部分,否则我想在那里报告这个错误。

以上是关于$filter 在带有 MySQL 的 JPA/Olingo 2.0.11 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

JavaWebFilter如果debug带有Filter过滤器的项目

检查带有 WMS 的 CQL_FILTER 是不是返回至少一项功能

Django_filters - 未显示带有 ForeignKey 的 MultipleChoiceFilter

你如何使用带有参数列表的 django-filter 包?

是否有任何支持 SQLite(在 Android 上)的良好 ORM(最好是 JPA 实现)? [关闭]

为啥 Chrome 不显示带有 <use> 和 Filter="url(#id)" 属性的 SVG?