Spring Data JPA 自定义对象接收查询结果集
Spring Data JPA
简介
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦
自定义对象接收
在实际工作中,我们经常遇到的场景是查询数据表中的某几个字段,但是使用 Spring Data Jpa框架一般查询结果只返回对应的Entity实体。我们是无法直接操作 Spring Data JPA提供的一些方法获得自定义实体类。
例如。我们如果想拿用户表中的姓名和生日字段,我们可能写出如下的操作。
// entity 实体类
public class User {
private Stirng name;
private String birthday;
/**
* 其他字段
*/
}
public class UserDTO {
private Stirng name;
private String birthday;
/**
* 省略get set方法
*/
}
// dao层
public interface UserDao extends JpaSpecificationExecutor<User>,
JpaRepository<User, Integer> {
@Query("select name, birthday from user where name = ?1", nativeQuery=true)
List<UserDTO> findByName(String name);
}
实际测试可以测到返回的是Object[]类型,并非我们想要得到的UserDTO对象。那么我们应该如何操作呢
当然,我们也可以这样操作,用Object[]接收,然后对数组进行解析。
//转换实体类
public static <T> List<T> castEntity(List<Object[]> list, Class<T> clazz) throws Exception {
List<T> returnList = new ArrayList<T>();
if(CollectionUtils.isEmpty(list)){
return returnList;
}
Object[] co = list.get(0);
Class[] c2 = new Class[co.length];
//确定构造方法
for (int i = 0; i < co.length; i++) {
if(co[i]!=null){
c2[i] = co[i].getClass();
}else {
c2[i]=String.class;
}
}
for (Object[] o : list) {
Constructor<T> constructor = clazz.getConstructor(c2);
returnList.add(constructor.newInstance(o));
}
return returnList;
}
实体类
@Entity
@Table(name = "user")
@Data
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// 姓名
@Column(name = "name")
private String name;
// 昵称
@Column(name = "nickname")
private String nickname;
// 出生日期
@Column(name = "birthday")
private LocalDate birthday;
// 性别
@Column(name = "sex")
private Boolean sex;
//密码
@Column(name = "password")
private String password;
}
我们想要查询数据表中的某几个字段,有以下三种方法。
Spring Data Jpa支持的关键字搜索
自定义一个返回对象
@Data
@ToString
//@NoArgsConstructor 不可写,否则会出现类型转换错误
@AllArgsConstructor // 必须写,转换时会调用
public class UserDTO implements Serializable { private String name; private String nickname; private LocalDate birthday; }
Dao层定义 repository类
注意
- 这种方法存在局限性,只能使用JPA提供的一些方法。如果业务复杂,或者查询条件为空等,则无法处理
public interface UserDao extends JpaSpecificationExecutor<User>,
JpaRepository<User, Integer> {
/**
* 根据id查询
* @param id
* @param type
* @param <T>
* @return
*/
<T> Optional<T> findById(int id, Class<T> type);
}
测试
测试
@Autowired
private UserDao dao;@Test
public void findByIdTest() {
Optional<UserDTO> userDTO = dao.findById(1, UserDTO.class);
if (userDTO.isPresent()) {
System.out.println(userDTO);
}
}
自定义一个返回对象
需求:查询用户姓名,昵称,出生日期、
自定义一个返回对象
@Data
@ToString
//@NoArgsConstructor 不可写,否则会出现类型转换错误
@AllArgsConstructor // 必须写,转换时会调用
public class UserDTO implements Serializable { private String name; private String nickname; private LocalDate birthday; }
Dao层定义 repository类 ,使用@Query注解,JPQL
- 注意:
- 当返回对象为实体类中的某几个属性时,字段名和实体类必须保持一致。否则无法映射
- 如果实体类中的属性名为驼峰例如:userName,映射到数据库则为user_name
- 使用全限定类名com.kxj.jpa.dto.UserDTO,必须有new关键字
public interface UserDao extends JpaSpecificationExecutor<User>,JpaRepository<User, Integer> {
/**
* 根据用户名查找
* @param name
* @return
*/
@Query(value = "select new com.kxj.jpa.dto.UserDTO(u.name, u.nickname, u.birthday) from User u where u.name = :name")
List<UserDTO> findByUserName(@Param("name") String name);
}
测试
测试
@Test
public void findByUserNamesTest() {
List<UserDTO> userDTOS = dao.findByUserName("tom");
for (UserDTO userDTO : userDTOS) {
System.out.println(userDTO);
}
}
自定义接口
自定义一个接口
public interface UserDTO2 {
String getName();/**
* 当别名与该getXXX名称不一致时,可以使用该注解调整
* @return
*/
@Value("#{target.nickName}")
String getNickname(); LocalDate getBirthday(); // 主要起打印作用
default String toStringInfo() {
return getName() + " " + getNickname() + " " + getBirthday();
}
}
Dao层定义 repository类 ,使用@Query注解,JPQL
注意:
当返回对象为接口时,SQL语句中查询的字段为 类.字段 ,eg: u.name . 则必须写别名,和接口中getXXX保持一致,否则无法转换,为null。
如果别名与该getXXX名称不一致时,也可在接口中使用@Value("#{target.XXX}")和SQL中别名保持一致
public interface UserDao extends JpaSpecificationExecutor<User>,
JpaRepository<User, Integer> {
/**
* 根据用户名查找
* 别名必须写 在HQL方式中 否则无法转换 为null,在原生SQL中别名可写可不写
* @param name
* @return
*/
@Query(value = "select u.name as name, u.nickname as nickName, u.birthday as birthday from User u where u.name = :name")
List<UserDTO2> findByUserName(@Param("name") String name);
}
测试
@Test
public void findByNameTest() {
List<UserDTO2> list = dao.findByUserName("tom");
list.forEach(user -> System.out.println(user.toStringInfo()));
}
如果我们想获取Page对象,则查询的时候加上Pageable对象
注意
传入Pageable对象,JPA会自动帮我们解析。查询的时候回多出一条查询条数的SQL
/**
* 分页 根据用户名查找
* @param name
* @param pageable
* @return
*/
@Query(value = "select u.name as name, u.nickname as nickName, u.birthday as birthday from User u where u.name = :name") Page<UserDTO2> findByUserName(@Param("name") String name, Pageable pageable);
测试
注意
当数据表中的查询总条数大于传入的size(每页显示的条数时,才会触发查询总条数
@Test
public void findByNamePageTest() {
Pageable pageable = PageRequest.of(0, 1);
Page<UserDTO2> page = dao.findByUserName("tom", pageable);
page.getContent().forEach(user -> System.out.println(user.toStringInfo()));
System.out.println("条数:" + page.getTotalElements());
}
通过配置输出SQL,可以在控制台看到输出的SQL语句是两条
6. 使用原生SQL
我们除了使用JPQL查询外,也可以使用原生SQL进行查询
/**
* 分页 根据用户名查找 原生SQL
* @param name
* @param pageable
* @return
*/
@Query(value = "select name, nickName, birthday from user where name = :name", nativeQuery = true)
Page<UserDTO2> findByUserNameUseNativeSQL(@Param("name") String name, Pageable pageable);
- 测试同上例
处理实体类中未有的属性
需求:查询用户重名的个数及姓名
自定义对象
- 用法大致和上述无区别,知识多加属性和查询的字段保持一致
- 注意
- 注意:count()查询返回的数据类型是Long类型,如果用其他类型接收,会报错
- 注意
- 用法大致和上述无区别,知识多加属性和查询的字段保持一致
@Data
@ToString
@AllArgsConstructor // 必须写,转换时会调用
public class UserDTO3 { private String name; /**
* 数量
*/
private Long count;
}
/**
* 查找名字相同的数量 类
* @return
*/
@Query(value = "select new com.kxj.jpa.dto.UserDTO3(u.name, count(u.id)) from User u group by u.name")
List<UserDTO3> findUserCount();
自定义接口
public interface UserDTO4 { String getName(); Long getCount(); default String toStringInfo() {
return getName() + " " + getCount();
}
}
/**
* 查找名字相同的数量 接口
* @return
*/
@Query(value = "select name, count(id) as count from user group by name", nativeQuery = true)
List<UserDTO4> findUserNameCount();
上面也可以使用原生SQL查询
以上测试省略
源码
https://github.com/kong0827/Spring-Data-JPA/tree/master/Spring-Data-Jpa-Enhance
总结
- 采用自定义对象接收时,可以分场景使用不同方式
- 自定义对象和自定义接口的区别,例如自定义对象,属性要和实体类保持一致,要有全参构造等
- 如果遇到聚集函数等实体类没有定义的属性应该添加字段相对应的属性到DTO类中
Spring Data JPA 自定义对象接收查询结果集的更多相关文章
- Spring Data JPA中的动态查询 时间日期
功能:Spring Data JPA中的动态查询 实现日期查询 页面对应的dto类private String modifiedDate; //实体类 @LastModifiedDate protec ...
- spring data jpa使用原生sql查询
spring data jpa使用原生sql查询 @Repository public interface AjDao extends JpaRepository<Aj,String> { ...
- 【Spring Data 系列学习】Spring Data JPA 自定义查询,分页,排序,条件查询
Spring Boot Jpa 默认提供 CURD 的方法等方法,在日常中往往时无法满足我们业务的要求,本章节通过自定义简单查询案例进行讲解. 快速上手 项目中的pom.xml.application ...
- Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!
前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...
- Spring Data Jpa (四)注解式查询方法
详细讲解声明式的查询方法 1 @Query详解 使用命名查询为实体声明查询是一种有效的方法,对于少量查询很有效.一般只需要关心@Query里面的value和nativeQuery的值.使用声明式JPQ ...
- spring data jpa自定义bean字段映射
当遇到复杂多表查询时,并且同时还需要确保查询性能,此时则需要使用自定义sql查询,然而spring data jpa对于自定义sql则需使用查询需要在对应的bean中做太多的配置映射,我尝试了一下,最 ...
- Spring MVC和Spring Data JPA之按条件查询和分页(kkpaper分页组件)
推荐视频:尚硅谷Spring Data JPA视频教程,一学就会,百度一下就有, 后台代码:在DAO层继承Spring Data JPA的PagingAndSortingRepository接口实现的 ...
- Spring Data Jpa的四种查询方式
一.调用接口的方式 1.基本介绍 通过调用接口里的方法查询,需要我们自定义的接口继承Spring Data Jpa规定的接口 public interface UserDao extends JpaR ...
- spring data jpa实现多条件查询(分页和不分页)
目前的spring data jpa已经帮我们干了CRUD的大部分活了,但如果有些活它干不了(CrudRepository接口中没定义),那么只能由我们自己干了.这里要说的就是在它的框架里,如何实现自 ...
随机推荐
- webdriver访问各个浏览器驱动下载及安装
这里首先需要查看一下自己安装的selenium版本 查看步骤: windows系统打开cmd命令行输入:pip show selenium查看结果如下: 进入到selenium官网查看版本信息 sel ...
- HashMap的四种遍历!
HashMap的四种遍历 import java.util.Collection; import java.util.HashMap; import java.util.Map; import jav ...
- 基于SR-IOV的IO虚拟化技术
服务器配置要求 x86服务器内存不能低于32GB 服务器CPU需要支持虚拟化和设备虚拟化 VT-x VT-d,SR-IOV 功能,并且在BIOS中能启用了SR-IOV 网卡配置最起码为千兆配置 支持 ...
- windows应用程序单实例
前言 这才第几天博客就跟不上了,看来一天一篇博客的目标还是有点大,写博客还是挺费时间的,写了不满意删,删完再写...直到自己没了耐心.今天先写个前言,实质性的内容明天再补吧.今天一天的收获还是挺多的, ...
- Bitstream or PCM?
背景 提问 讨论精选 一 二 三 四 五 最后 电视上同轴输出的做法. 背景 USB通道下播放声音格式为AAC的视频文件,同轴输出设置为Auto,功放没有声音,设置成PCM,有声音. 提问 Auto/ ...
- C++扬帆远航——11(斐波那契数列)
/* * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:Feibo.cpp * 作者:常轩 * 微信公众号:Worldh ...
- C++走向远洋——33(静态成员的应用)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:time.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- LeetCode 题解 | 1. 两数之和
题目描述: 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 15], t ...
- c语言之单向链表
0x00 什么是链表 链表可以说是一种最为基础的数据结构了,而单向链表更是基础中的基础.链表是由一组元素以特定的顺序组合或链接在一起的,不同元素之间在逻辑上相邻,但是在物理上并不一定相邻.在维护一组数 ...
- git工作中常用操作总结
这篇文章主要记录下工作中常用的git操作.主要是对之前文章记录的问题做个总结,这个其实在idea中操作更加简单 别名配置 在敲git 命令时,其实可以使用别名,比如 commit可以配置为ci 下面是 ...