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支持的关键字搜索

  1. 自定义一个返回对象

    @Data
    @ToString
    //@NoArgsConstructor 不可写,否则会出现类型转换错误
    @AllArgsConstructor // 必须写,转换时会调用
    public class UserDTO implements Serializable { private String name; private String nickname; private LocalDate birthday; }
  2. Dao层定义 repository类

  3. 注意

  • 这种方法存在局限性,只能使用JPA提供的一些方法。如果业务复杂,或者查询条件为空等,则无法处理
  1. 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);
    }
  2. 测试

  3. 测试

    @Autowired

    private UserDao dao;

    @Test
    public void findByIdTest() {
    Optional<UserDTO> userDTO = dao.findById(1, UserDTO.class);
    if (userDTO.isPresent()) {
    System.out.println(userDTO);
    }
    }

自定义一个返回对象

需求:查询用户姓名,昵称,出生日期、

  1. 自定义一个返回对象

    @Data
    @ToString
    //@NoArgsConstructor 不可写,否则会出现类型转换错误
    @AllArgsConstructor // 必须写,转换时会调用
    public class UserDTO implements Serializable { private String name; private String nickname; private LocalDate birthday; }
  2. Dao层定义 repository类 ,使用@Query注解,JPQL

    1. 注意:
    • 当返回对象为实体类中的某几个属性时,字段名和实体类必须保持一致。否则无法映射
    • 如果实体类中的属性名为驼峰例如:userName,映射到数据库则为user_name
    • 使用全限定类名com.kxj.jpa.dto.UserDTO,必须有new关键字
  3. 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);
    }
  4. 测试

  5. 测试

    @Test
    public void findByUserNamesTest() {
    List<UserDTO> userDTOS = dao.findByUserName("tom");
    for (UserDTO userDTO : userDTOS) {
    System.out.println(userDTO);
    }
    }

自定义接口

  1. 自定义一个接口

    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);
    }
  1. 测试

    @Test
    public void findByNameTest() {
    List<UserDTO2> list = dao.findByUserName("tom");
    list.forEach(user -> System.out.println(user.toStringInfo()));
    }
  2. 如果我们想获取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);

  3. 测试

    注意

    • 当数据表中的查询总条数大于传入的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);
  1. 测试同上例

处理实体类中未有的属性

需求:查询用户重名的个数及姓名

  • 自定义对象

    1. 用法大致和上述无区别,知识多加属性和查询的字段保持一致

      • 注意

        1. 注意:count()查询返回的数据类型是Long类型,如果用其他类型接收,会报错
    1. @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

总结

  1. 采用自定义对象接收时,可以分场景使用不同方式
  2. 自定义对象和自定义接口的区别,例如自定义对象,属性要和实体类保持一致,要有全参构造等
  3. 如果遇到聚集函数等实体类没有定义的属性应该添加字段相对应的属性到DTO类中

Spring Data JPA 自定义对象接收查询结果集的更多相关文章

  1. Spring Data JPA中的动态查询 时间日期

    功能:Spring Data JPA中的动态查询 实现日期查询 页面对应的dto类private String modifiedDate; //实体类 @LastModifiedDate protec ...

  2. spring data jpa使用原生sql查询

    spring data jpa使用原生sql查询 @Repository public interface AjDao extends JpaRepository<Aj,String> { ...

  3. 【Spring Data 系列学习】Spring Data JPA 自定义查询,分页,排序,条件查询

    Spring Boot Jpa 默认提供 CURD 的方法等方法,在日常中往往时无法满足我们业务的要求,本章节通过自定义简单查询案例进行讲解. 快速上手 项目中的pom.xml.application ...

  4. Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!

    前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...

  5. Spring Data Jpa (四)注解式查询方法

    详细讲解声明式的查询方法 1 @Query详解 使用命名查询为实体声明查询是一种有效的方法,对于少量查询很有效.一般只需要关心@Query里面的value和nativeQuery的值.使用声明式JPQ ...

  6. spring data jpa自定义bean字段映射

    当遇到复杂多表查询时,并且同时还需要确保查询性能,此时则需要使用自定义sql查询,然而spring data jpa对于自定义sql则需使用查询需要在对应的bean中做太多的配置映射,我尝试了一下,最 ...

  7. Spring MVC和Spring Data JPA之按条件查询和分页(kkpaper分页组件)

    推荐视频:尚硅谷Spring Data JPA视频教程,一学就会,百度一下就有, 后台代码:在DAO层继承Spring Data JPA的PagingAndSortingRepository接口实现的 ...

  8. Spring Data Jpa的四种查询方式

    一.调用接口的方式 1.基本介绍 通过调用接口里的方法查询,需要我们自定义的接口继承Spring Data Jpa规定的接口 public interface UserDao extends JpaR ...

  9. spring data jpa实现多条件查询(分页和不分页)

    目前的spring data jpa已经帮我们干了CRUD的大部分活了,但如果有些活它干不了(CrudRepository接口中没定义),那么只能由我们自己干了.这里要说的就是在它的框架里,如何实现自 ...

随机推荐

  1. 基于OpenDDS应用程序开发(3)订阅端实现

    连续的三篇博文演示如何基于OpenDDS开发应用程序,将数据从发布端节点发送到订阅端节点,该示例程序由一个发布者发布数据,一个订阅者订阅数据,使用默认的QoS策略和TCP/IP传输方式. 本文是第三篇 ...

  2. 垃圾回收GC

    ​ 每种语言都有自己的垃圾回收机制.接下来我们来讲一下python的垃圾回收机制. 小整数对象池:python对小整数的定义为[-5,257),这些整数对象是提前建立好的,不会被垃圾回收.单个字母也一 ...

  3. Proto3使用指南

    这篇指南讲述如何使用Protocol Buffers来结构化你的Protocol Buffer数据,包括.proto文件语法以及如何从.proto文件生成你的访问类型.本文主要涵盖了proto3的语法 ...

  4. RxJava学习笔记(操作符)

    前言 上一篇文章介绍了RxJava的基础知识和简单实现,篇幅已经比较多了,所以把操作符(Operators)相关的内容放在这一篇.有了上一篇文章的基础,相信会比较容易理解操作符相关的内容了. 操作符( ...

  5. Python——4Dict和Set类型

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  6. TCP可靠传输的工作原理

    TCP可靠传输的工作原理 一.停止等待协议 1.1.简介 在发送完一个分组后,必须暂时保留已发送的分组的副本. 分组和确认分组都必须进行编号. 超时计时器的重传时间应当比数据在分组传输的平均往返时间更 ...

  7. js案例之使用正则表达式进行验证数据正确性

    #js案例之使用正则表达式进行验证数据正确性 代码上传至 "GitHub" 样例: <tr> <td>密码:</td> <td> & ...

  8. 一起了解 .Net Foundation 项目 No.11

    .Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. Microsoft Web ...

  9. iMX287A基于嵌入式Qt的新冠肺炎疫情监控平台

    目录 1.前言 2.数据接口的获取 3.Qt界面的实现 4.在开发板上运行Qt程序 5.最终效果 6.代码下载 @ 1.前言 之前我使用在桌面版本Qt实现了肺炎疫情监控平台:基于Qt的新冠肺炎疫情数据 ...

  10. 小白自学机器学习----3.令人头秃的pytorch安装 (No module named 'tools.nnwrap' 错误)

    tensorflow 刚刚会写基础的模块了,今天找到研究方向的代码是pytorch实现的 总是看到这句话,人生苦短,我用pytorch 看来pytorch应该比tensorflow好学,但是!! py ...