$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 使用olingo2
、jpa
和spring-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.java
和 Application.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