引言: Spring Boot号称微服务的利器,在结合了Spring Data与JPA之后,更是如虎添翼,开发快速的不像话,本文将讲述一个关于JPA中一个诡异问题的诊断分析过程以及修复方法。

环境介绍

JDK 1.8 Spring 4.2 Spring Boot 1.5.9

问题描述

在Spring Data中的Repository接口中创建了一个检查数据是否存在的接口方法:

@Repository
public interface VideoEntityRepository extends JpaRepository<VideoEntity, Long> {
    ........
    public boolean existsByUserIdAndName(long userId, String name);
   }

VideoEntity的类如下:

@SuppressWarnings("serial")
@Table(name="flook_video")
@Entity
@Data
@EqualsAndHashCode(callSuper=true)
public class VideoEntity extends BaseEntity {
    @Column(name="user_id")
    private long userId;

    @Column(name="name")
    private String name;

    @Column(name="change_version")
    private double changeVersion;
}

在调用方法existsBy方法的时候,返回的结果一直为false, 结果不正确,在偶的期望中,其执行结果应该不会出错的?那问题出在哪里呢?

关于existsBy的介绍

Spring Data提供了若干非常实用的扩展,将数据库表日常的CRUD操作都进行很好的实现,并提供了若干扩展机制,基于一套简单易用的命名规则,来基于声明式实现场景的数据库查询操作:

  • countByColumName
  • existsByColumnName
    上述两种方式都是由Spring Data来帮助动态生成SQL的。

基于@Query方式

除了基于countBy/existsBy两种方式之外,可以直接使用@Query方式来标注特定的SQL或者JPQL来实现功能,由于这种方式需要些SQL,功能上完全覆盖,但是工作量略大,不是最优的方式。

问题分析

由于底层使用了Hibernate/JPA/Spring Data来实现的数据访问层的实现,所以,最好的方式当然是查看动态生成的SQL了,于是找到了生成的SQL语句:

select videoentit0_.id as col_0_0_ from video videoentit0_ where videoentit0_.user_id=? and videoentit0_.name=? limit ?

表的名称是video,这个名称是不对的。这个情况是怎么发生的呢?

问题的解决

经过分析发现,是由于在代码中存在两个类名完全相同VideoEntity的类,虽然在Repository中我们的的确确没有引用错误相关的,问题应该出在当Spring Data碰到两个相同的类名之时,其实不知道如何来生成SQL的,换句话说,其应该是基于类名而非类路径来动态生成执行SQL的。

TODO: 基于源代码中找到相关部分内容。

问题解决

将当前的VideoEntity重新命名,确保不存在重名的问题,即使类的路径不一样,但是类名一样,也是会产生这样的问题。
从一个侧面来分析,在Spring Data中所有的DataBean都是需要使用全路径的类名的,否则同样会出现问题。

参考资料

  1. https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐