使用JPA查询List<Map>时因为别名导致查询报错java.lang.IndexOutOfBoundsException: Index: 1, Size: 1

原创
2020/12/27 16:25
阅读数 5K

1、问题描述

jpa hibernate 都是我们的经常使用的ORM框架,但是不能完全满足自己的查询需求,所以我们会用自己定义SQL。在JPA使用注解进行实现 例如:

@Query(value =    "SELECT NAME,CODE,
                    FROM TABLE1
                    WHERE a.XGBZ <> '3'
                    AND id = :id",nativeQuery = true)
List<Map<String, Object>> getUserById(@Param("id") String id);
  • 但是发现这样写是有问题的,会抛出IndexOutOfBoundsException

2、问题分析

经过分析,JPA执行原生SQL,将数据封装成List<Map> 时需要使用Tuple 进行封装。

/**
 * Interface for extracting the elements of a query result tuple.
 *
 * @see TupleElement
 * @since Java Persistence 2.0
 */
public interface Tuple {

如果不写别名的话,导致JPA走默认的字段映射。但是我们接口注解,没有指定返回策略。

  • PS: 如果有实现类可以使用指定。
query.setResultTransformer(AliasedTupleSubsetResultTransformer.INSTANCE).list();

3、解决办法

写SQL要规范,要使用别名,条件和字段上都要加上别名并且要规范使用

4、具体源码分析

核心代码org.springframework.data.jpa.repository.query.NativeJpaQuery

@Nullable
private Class<?> getTypeToQueryFor() {

	ResultProcessor resultFactory = getQueryMethod().getResultProcessor();
	ReturnedType returnedType = resultFactory.getReturnedType();

	Class<?> result = getQueryMethod().isQueryForEntity() ? returnedType.getDomainType() : null;
	// 如果没有别名this.getQuery().isDefaultProjection() 会返回true
	// resul就会返回空
	if (this.getQuery().hasConstructorExpression() || this.getQuery().isDefaultProjection()) {
		return result;
	}

	return returnedType.isProjecting() && !getMetamodel().isJpaManaged(returnedType.getReturnedType()) //
			? Tuple.class
			: result;
}

isDefaultProjection()的判断逻辑如下

	/*
	 * Returns whether the query uses the default projection, i.e. returns the main alias defined for the query.
	 */
	@Override
	public boolean isDefaultProjection() {
		return getProjection().equals(alias);
	}

具体获取别名的逻辑如下 org.springframework.data.jpa.repository.query.QueryUtils;

QueryUtils.detectAlias(sql);

希望可以帮到你

展开阅读全文
加载中
点击加入讨论🔥(3) 发布并加入讨论🔥
打赏
3 评论
0 收藏
0
分享
返回顶部
顶部