背景:本人不会 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 语句就不对了?如果是使用姿势不对,那该怎么解决啊?[快哭了]
1
duwan 2020-05-28 17:20:03 +08:00
你把参数 Collection<Status>换成 Collection<Integer>不就行了?
|
2
duwan 2020-05-28 17:22:40 +08:00
然后,先将 Collection<Status>转换成 Collection<Integer>,再调用
|
4
lybcyd 2020-05-28 17:52:22 +08:00 via Android
JPA 把枚举填进 SQL 不会自动转换吧。你手动转换成 int 就可以了。不过建议你看看 jpa 自动生成的语句为什么不会用到正确的索引,还需要手动指定索引。
|
5
hantsy 2020-05-28 17:54:42 +08:00
nativeQuery 基本对应 Jdbc 语句,执行数据库原始的 SQL 。
在用 JPA 真的很少用。忘记不了 SQL,还不如直接用 jdbc 。Spring 的 Jdbc 封装真的不错,另外 Spring Data Jdbc 相当于一个简单的 ORM 框架。 |
6
snailsir OP @lybcyd 但是第一个非 nativeQuery 的方式,生成的 `status in (1,2,3)` 就是正确的啊。只是下面使用 nativeQuery 的时候,就不正常了。
手动转换成 int 怎么搞呀,本人不太会 java [哎] |
7
snailsir OP @hantsy 这个是老项目,使用的 JPA,目前都不想动了,只是维护,改改 bug 的那种样子,本人也不大会 java,所以换基本上是不可能的 [无奈]
|
8
kevinWHX 2020-05-29 07:41:00 +08:00 via iPhone
UPLOADING.ordinal() =1
|
9
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 |