Spring Data JPA nativeQuery 的问题,在线跪求大佬解答。

2020-05-28 16:53:39 +08:00
 snailsir

背景:本人不会 java,但是基本上能够看懂逻辑,维护一个老 java 项目,现在遇到一个慢查询的问题( mysql 最后选择了一个错误的索引),不考虑数据库表的修改,索引都是正常的。

spring-data-jpa 版本:1.6.5 。

问题描述如下:

首先 status 是一个枚举。然后有下面有两个基于 JPA 查询数据的方法

public enum Status {
    UPLOADING,  // 0
    PROCESSING, // 1
    PUBLISHED,  // 2
    FAILED,     // 3
    DELETED;    // 4
}    

// 方法一:生成的 sql 存在慢查询问题( mysql 选择了一个错误的索引)
Slice<Document> findByUserIdAndStatusIn(String userId, Collection<Status> status, Pageable pageable);

// 方法二:使用 nativeQuery,指定使用的索引
@Query(value = "select * from #{#entityName} use index (userId_createTime_idx) "
                + " where userId = :userId and status in :status "
                + " order by createTime desc "
                + " limit :offset,:size",
    nativeQuery = true)
List<Document> findByUserIdAndStatusInUseIndex(@Param("userId") String userId,
                                                @Param("status") Collection<Status> status,
                                                @Param("offset") int limitOffset,
                                                @Param("size") int limitSize);

第一个方法最后生成的 sql 语句是正常的,比如

status in (1,2,3)

第二个方法是新增的,使用 nativeQuery 指定使用索引,但基于同样的参数Collection<Status> status生成的 sql 语句,就不正常,比如

// 里面的...翻译后,涉及敏感信息,故用...代替
status in (
    x'...2E706C6174666F726D2E6D6F64656C2E646F63756D656E742E53746174757300000000000000001200007872000E6A6176612E6C616E672E456E756D0000000000000000120000787074000A50524F43455353494E47', 
    x'...2E706C6174666F726D2E6D6F64656C2E646F63756D656E742E53746174757300000000000000001200007872000E6A6176612E6C616E672E456E756D000000000000000012000078707400095055424C4953484544', 
    x'...2E706C6174666F726D2E6D6F64656C2E646F63756D656E742E53746174757300000000000000001200007872000E6A6176612E6C616E672E456E756D000000000000000012000078707400064641494C4544') 

这几个莫名的字符,经过翻译之后,是这样的

.platform.model.document.Status           xr  java.lang.Enum           xpt 
PROCESSING

.platform.model.document.Status           xr  java.lang.Enum           xpt 	PUBLISHED

.platform.model.document.Status           xr  java.lang.Enum           xpt  FAILED

所以,使用 nativeQuery 之后,为什么同样的参数Collection<Status> status最后生成的 sql 语句就不对了?如果是使用姿势不对,那该怎么解决啊?[快哭了]

2309 次点击
所在节点    Java
11 条回复
duwan
2020-05-28 17:20:03 +08:00
你把参数 Collection<Status>换成 Collection<Integer>不就行了?
duwan
2020-05-28 17:22:40 +08:00
然后,先将 Collection<Status>转换成 Collection<Integer>,再调用
snailsir
2020-05-28 17:45:55 +08:00
@duwan 怎么转换啊
lybcyd
2020-05-28 17:52:22 +08:00
JPA 把枚举填进 SQL 不会自动转换吧。你手动转换成 int 就可以了。不过建议你看看 jpa 自动生成的语句为什么不会用到正确的索引,还需要手动指定索引。
hantsy
2020-05-28 17:54:42 +08:00
nativeQuery 基本对应 Jdbc 语句,执行数据库原始的 SQL 。

在用 JPA 真的很少用。忘记不了 SQL,还不如直接用 jdbc 。Spring 的 Jdbc 封装真的不错,另外 Spring Data Jdbc 相当于一个简单的 ORM 框架。
snailsir
2020-05-28 18:07:50 +08:00
@lybcyd 但是第一个非 nativeQuery 的方式,生成的 `status in (1,2,3)` 就是正确的啊。只是下面使用 nativeQuery 的时候,就不正常了。

手动转换成 int 怎么搞呀,本人不太会 java [哎]
snailsir
2020-05-28 18:09:42 +08:00
@hantsy 这个是老项目,使用的 JPA,目前都不想动了,只是维护,改改 bug 的那种样子,本人也不大会 java,所以换基本上是不可能的 [无奈]
kevinWHX
2020-05-29 07:41:00 +08:00
UPLOADING.ordinal() =1
Jrue0011
2020-05-29 10:09:49 +08:00
不太了解 SPRING DATA JPA,似乎用了 nativeQuery 后就不支持枚举的处理,可能作为 Object 序列化了。

解决办法:

1 、参数改成 Collections<Integer>,然后调用的地方手动将 Collections<Status>转成 Collections<Integer>

2 、参考这个定义一个接口和实现,实现注册 bean,可以注入 jdbcTemplate 做某些操作,最后让这个 repository 继承自定义接口就可以了。

https://docs.spring.io/spring-data/jpa/docs/1.6.5.RELEASE/reference/html/repositories.html#repositories.single-repository-behaviour
snailsir
2020-05-29 13:38:24 +08:00
@Jrue0011 嗯,我也是感觉使用 nativeQuery 之后,枚举就不行了。目前在考虑将 Collections<Status> 转成 Collections<Integer>,然后再试试了。
snailsir
2020-05-29 13:39:17 +08:00
@Jrue0011 但是不知道为啥使用 nativeQuery 之后,枚举就不行了,哪里出了问题。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/676373

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX