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接口中没定义),那么只能由我们自己干了.这里要说的就是在它的框架里,如何实现自 ...
随机推荐
- 吴裕雄--天生自然KITTEN编程:青蛙答题过河
- 吴裕雄--天生自然 R语言开发学习:分类(续一)
#-----------------------------------------------------------------------------# # R in Action (2nd e ...
- mysql 系列错误解决
参考文章来源 https://segmentfault.com/a/1190000015678751 https://blog.csdn.net/Tong_zhi/article/details/84 ...
- Python如何规避全局解释器锁(GIL)带来的限制
编程语言分类概念介绍(编译型语言.解释型语言.静态类型语言.动态类型语言概念与区别) https://www.cnblogs.com/zhoug2020/p/5972262.html Python解释 ...
- sycPHPCMS v1.6 cookie sqlinjection
./user/index.php include "../include/conn.php"; include "../include/function.php" ...
- java里面的设计模式
文章目录 Creational(创建模式) 1. Abstract factory: 2. Builder: 3. Factory: 4. Prototype: 5. Singleton: 6. Ch ...
- 20170220-coroutine
协程 coroutine 最近频繁的听说到 "协程" 这个词,花了一段时间肤浅的研究了一下.对于 "它是一个什么东西" 有了一个大概的了解. from wiki ...
- Liferay7 Intellij IDEA 开发环境搭建
一.安装Liferay插件 安装过程不在赘述,推荐两种安装方式: 通过Intellij插件市场安装 通过下载插件zip包安装 安装完成后,在项目板块中点鼠标右键,会出现Liferay菜单. 二.安装L ...
- Java反射,泛型在Json中的运用
最近项目中遇到了Json数据自动获取的功能,不然令人想起java的反射,已经很长时间没复习java了正好一块连java的这一块内容一起过一遍.java中的反射无疑就相当于java开发者的春天,在众多的 ...
- 为什么MySQL分库分表后总存储大小变大了?
1.背景 在完成一个分表项目后,发现分表的数据迁移后,新库所需的存储容量远大于原本两张表的大小.在做了一番查询了解后,完成了优化. 回过头来,需要进一步了解下为什么会出现这样的情况. 与标题的问题的类 ...