V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
snailsir
V2EX  ›  Java

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

  •  
  •   snailsir · 2020-05-28 16:53:39 +08:00 · 2265 次点击
    这是一个创建于 1401 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景:本人不会 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 语句就不对了?如果是使用姿势不对,那该怎么解决啊?[快哭了]

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

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

    手动转换成 int 怎么搞呀,本人不太会 java [哎]
    snailsir
        7
    snailsir  
    OP
       2020-05-28 18:09:42 +08:00
    @hantsy 这个是老项目,使用的 JPA,目前都不想动了,只是维护,改改 bug 的那种样子,本人也不大会 java,所以换基本上是不可能的 [无奈]
    kevinWHX
        8
    kevinWHX  
       2020-05-29 07:41:00 +08:00 via iPhone
    UPLOADING.ordinal() =1
    Jrue0011
        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
    snailsir
        10
    snailsir  
    OP
       2020-05-29 13:38:24 +08:00
    @Jrue0011 嗯,我也是感觉使用 nativeQuery 之后,枚举就不行了。目前在考虑将 Collections<Status> 转成 Collections<Integer>,然后再试试了。
    snailsir
        11
    snailsir  
    OP
       2020-05-29 13:39:17 +08:00
    @Jrue0011 但是不知道为啥使用 nativeQuery 之后,枚举就不行了,哪里出了问题。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2871 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 77ms · UTC 13:07 · PVG 21:07 · LAX 06:07 · JFK 09:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.