$filter 在 JPA/Olingo 2.0.11 和 MySQL 中不工作

$filter not working in JPA/Olingo 2.0.11 with MySQL

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

我用olingo2jpaspring-boot基于这个GitHub repository做了一个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 '\',因为它没有根据数据库类型设置正确的方言!

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

请同时考虑以下几点:

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

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

/* 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 文件中执行此操作,但它似乎在请求处理时会被忽略!

没有解决原来的问题,也不是最有效的方法.. 但这里有一个解决方法,用于如何删除仅包含一个斜杠的不正确 'escape' 语句:

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?