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接口中没定义),那么只能由我们自己干了.这里要说的就是在它的框架里,如何实现自 ...
随机推荐
- 初等数论-Base-1(筛法求素数,欧拉函数,欧几里得算法)
前言 初等数论在OI中应用的基础部分,同机房的AuSquare和zhou2003君早就写完了,一直划水偷懒的Hk-pls表示很方,这才开始了这篇博客. \(P.S.\)可能会分部分发表. Base-1 ...
- zookeeper伪分布式集群搭建
zookeeper集群搭建注意点: 配置数据文件myid1/2/3对应server.1/2/3 通过zkCli.sh -server [ip]:[port]检测集群是否 ...
- 关于利用glassfish部署Java EE的firstcup-war时报错
在利用maven+glassfish打包部署Java EE的firstcup-war时报错 1.在利用glassfish部署firstcup-war.war文件时提示如下错误信息: 根据上面的错误提示 ...
- react router为什么推荐使用browserHistory而不推荐hashHistory?
首先 browserHistory 其实使用的是 HTML5 的 History API,浏览器提供相应的接口来修改浏览器的历史记录:而 hashHistory 是通过改变地址后面的 hash 来改变 ...
- 2.mac下 安装go-ethereum
Contents 上代码 A.前期准备:http://m.2cto.com/kf/201612/573010.html (1)安装python2.7,mac在终端中直接使用以下命令:brew inst ...
- zabbix监控mysql数据库信息脚本
---恢复内容开始--- 在/usr/local/zabbix/etc/zabbix_agentd.conf增加 # 获取mysql性能指标 UserParameter=mysql.status[*] ...
- HINOC2.0标准介绍(1):概述
本文首发于'瀚诺观察'微信公众号 摘要: 2016年3月18日,国家新闻出版广电总局批准发布了行业标准GY/T 297-2016<NGB宽带接入系统HINOC2.0物理层和媒体接入控制层技术规范 ...
- 这有一管信息量很大的DNA
题图:华盛顿大学副教授Luis Henrique Ceze(照片中的男士)和研究科学家Lee Organick正将数字数据保存进DNA测序,以供"读取"并追溯原始文件. 来自微软和 ...
- ORB-SLAM2 运行 —— ROS + Android 手机摄像头
转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12404730.html 本文要点: ROS 配置安装 解决 sud ...
- golang在debian下不能用sudo进行使用的问题
sudo ln -s /usr/local/go/bin/go /usr/bin/go 然后就ok了. 去查了下这两个路径的差别,也没查出什么.只是说/usr/bin 是系统预装所在的路径.