bug之 JPA采用原生sql查询 BigInteger can not case to Long

在这周对代码进行重构时,发现了一个很隐晦的bug,我认为很有必要把它记下来。产生bug详细流程如下:

  1. 首先我是采用JPA提供的执行原生sql语句的方式去查询一个ID,这个ID在数据库的类型为bigint。查询方式如下:
@Query(nativeQuery = true,
            value = "SELECT car_id FROM cars WHERE car_id IN (?1)")
List findCarIdsByCars(List carIds);

调用方式:

public List getCarIdByCars(List vehicleIds) {
    List list = quotationRepository.findCarIdsByCars(vehicleIds);
    return longList;
}

这个查询的目的是返回数据库已存在的指定ID。从SQL语句看是没有任何问题的,但是当我通过debug调用这个repository会发现如下内容:

debug时list内的值

显然,List的值竟然是BigInteger的,而我们上面写的明明是List,而且这种时候编译器也是没有报错的,明明对象的类型就是不对的!

寻找原因

通过google搜索发现,JPA内部查询是通过createSQLQuery和createQuery实现的。所有的查询都会调用这两个方法。它们的不同点是:

  1. createQuery用的JPQL语句进行查询,createSQLQuery用sql语句查询;
  2. 前者以hibernate生成的Bean为对象装入list返回;后者则是以对象数组进行存储;
  3. 当通过createSQLQuery查询时,如果存在Bean的实例,则它会按照实例去返回,但是当实例不存在时它会按照JPA给定的默认配置去返回映射值,而bigint的映射正是BigInteger,所以才会出现如上情况。

解决办法

解决办法有两种,一种是修改repository的返回方式为BigInteger,但是这样的话还需要调用代码去转换,而且可读性也不太好。所以建议采用第二种解决办法:
使用JPQL(Java Persistence Query Language)语言去写返回值。实现方式如下:

@Query("SELECT c.carId FROM cars c WHERE c.carId IN (?1)")
List findCarIdsByCars(List carIds);

这样写JPA会完美map返回值,其实跟写原生sql是一样的。另外个人觉得还是尽量用JPQL语句进行数据库操作比较好,因为JPA的存在一个重要的原因就是为了JAVA与数据库之间的解耦,我们通过JPQL语言可以很好地将table与java代码之间的耦合分离开来。何乐而不为呢?

你可能感兴趣的:(bug之 JPA采用原生sql查询 BigInteger can not case to Long)