本文使用了 SpringBoot 中的
spring-boot-starter-data-jpa模块来加入 JPA 功能,单独使用 Spring Data JPA 可能有差异,JDK 版本为 8
介绍
Spring Data JPA 是 Spring Data 项目众多子项目中的一个,是对 Java JPA 规范扩展,本身使用了 Hibernate 的 JPA 实现,并提供了更丰富的 API 及查询功能,最常用的方式有:
- JPA api,通过如 findById、 save、 findAll 等 API 进行查询
- @Query 查询,通过在 interface 方法申明注解实现查询
- Specification 查询,及 Criteria 查询
- JPQL 动态查询,手动构造 JPQL 语句进行查询
- Native SQL 动态查询,手动构造原生 SQL 语句进行查询
Native SQL
Native SQL 功能可以直接调用数据库原生的 SQL 进行查询,注意如果使用了数据库特定的语法和函数,那么程序就会失去移植性,一个最简单的 Native SQL例子:
@PersistenceContext
EntityManager entityManager;
public List findByNativeSQL() {
String sql = "select * from user";
Query query = entityManager.createNativeQuery(sql);
List list = query.getResultList();
return list;
}
增加查询参数后:
String sql = "select * from user where name like :name";
Query query = entityManager.createNativeQuery(sql);
query.setParameter("name", "%诱%");
List list = query.getResultList();
return list;
上面使用的 getResultList 返回的结果集是 List 格式,如果需要映射成 Entity 对象,则:
Query query = entityManager.createNativeQuery(sql, User.class);
但是其中类 User 必须要通过 @Entity 注解注册为 JPA 实体对象
Transformer
如果需要转换成任意非 JPA 对象或者 Map 则需要用到 ResultTransformer:
// 转换为 List<MyUser>
query.unwrap(NativeQueryImpl.class)
.setResultTransformer(Transformers.aliasToBean(MyUser.class));
List list = query.getResultList();
注意MyUser由于没有映射关系,所以类的字段名必须要和 SQL 语句中的列名一致,当然可以使用列as别名来避免这个问题
// 转换为 List<Map>
query.unwrap(NativeQueryImpl.class)
.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List list = query.getResultList();
Hibernate默认提供三种ResultTransformer:
- Transformers.ALIAS_TO_ENTITY_MAP 结果集转为 List<Map>
- Transformers.TO_LIST 结果集转为 List<List>
- Transformers.aliasToBean(TargetClass.class) 结果集转为 List<TargetClass>
如果默认提供的三种不能满足要求,可以自定义ResultTransformer,这里仿照Transformers.TO_LIST的ToListResultTransformer.java来构建一个ToStringResultTransformer.java
先看下org.hibernate.transform.ToListResultTransformer.java的实现:
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.transform;
import java.util.Arrays;
/**
* Tranforms each result row from a tuple into a {@link java.util.List} whose elements are each tuple value
*/
public class ToListResultTransformer extends BasicTransformerAdapter {
public static final ToListResultTransformer INSTANCE = new ToListResultTransformer();
/**
* Disallow instantiation of ToListResultTransformer.
*/
private ToListResultTransformer() {
}
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
return Arrays.asList( tuple );
}
/**
* Serialization hook for ensuring singleton uniqueing.
*
* @return The singleton instance : {@link #INSTANCE}
*/
private Object readResolve() {
return INSTANCE;
}
}
然后仿照着写一个ToStringResultTransformer.java
package com.example.trasformer;
import org.hibernate.transform.BasicTransformerAdapter;
import java.util.Arrays;
public class ToStringResultTransformer extends BasicTransformerAdapter {
public static final ToStringResultTransformer INSTANCE = new ToStringResultTransformer();
private ToStringResultTransformer() {
}
/**
* 每行一个 String,字段值全部接在一起
*
* @param tuple 字段值
* @param aliases 字段名
* @return
*/
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
StringBuilder row = new StringBuilder();
Arrays.asList(tuple).stream().forEach(row::append);
return row.toString();
}
private Object readResolve() {
return INSTANCE;
}
}
调用:
// 转换为 List<String>
return query.unwrap(NativeQueryImpl.class)
.setResultTransformer(ToStringResultTransformer.INSTANCE).list();
可以链式调用 .list() 直接返回结果,更复杂的 ResulltTransformer 就各凭本事了 ?
总结
Native SQL 很有用,特别是针对统计和多表关联查询
有问题欢迎留言咨询
EOL
本文介绍了如何在Spring Boot中利用Spring Data JPA进行原生SQL查询,探讨了Native SQL的使用场景及其可能带来的移植性问题。同时,文章详细讲解了Transformer在将查询结果转换为非JPA实体或Map时的角色,包括Hibernate提供的默认转换器和自定义转换器的实现方法。最后,作者强调了Native SQL在统计和复杂关联查询中的实用性。

6285

被折叠的 条评论
为什么被折叠?



