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. Luogu_1080_国王游戏

    题目描述 恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这n位大臣排成一排,国王站在队伍的最前面.排好队 ...

  2. BUG漏测的原因总结,以及如何处理

    一.漏测的概率 漏测,是指软件产品的缺陷没有在测试过程中被发现,而是在版本发布之后,用户在使用过程中发现存在的缺陷. 二.预防漏测的意义 我们都知道,缺陷越早被发现,发现和解决缺陷所花的成本就越小,如 ...

  3. 2020 倒计时 1 天,Python 工程师找工作更难了?

    Python 是最神奇的编程语言. 无意引战,我说的是"神奇",不是"最好",并不想去"撼动" PHP 的地位.               ...

  4. MySQL5.6 数据库主从(Master/Slave)同步安装与配置详解

    .安装环境 .基本环境配置 .Master的配置 .Slave的配置 .添加需要同步的从库Slave .真正的测试 安装环境 1 操作系统 :CentOS 6.5 2 数据库版本:MySQL 5.6. ...

  5. 人工智能VS投资者:股票市场上谁的胜算更高?

    人工智能研究历史渊源,当人工智能与资本投资,尤其是股票投资相结合或许听起来有些异想天开,但正如科幻作家William Gibson所言:"未来已经到来,只是分布不均." 在股票市场 ...

  6. 从租人APP沦为性工作发布平台 看共享经济监管边界

    看共享经济监管边界" title="从租人APP沦为性工作发布平台 看共享经济监管边界"> 继直播类软件部分涉黄之后,最近火爆的各类"租人"软件 ...

  7. 正则表达式之RegExp对象

    1.定义 RegExp对象是原生JavaScript中表示正则表达式的对象:是正则表达式(regular expression)的简写.有一点需要明确知道,正则表达式只能对字符串进行操作. 2.语法 ...

  8. 【简单版】hexo博客搭建流程梳理

    前言 本文章会为你梳理一个搭建hexo博客的流程 相关网址: Docs: https://hexo.io/docs/ Themes: https://hexo.io/themes/ 安装hexo 准备 ...

  9. USB小白学习之路(6) IIC EEPROM读取解析

    IIC EEPROM读取解析 1. 编译错误处理(这里可以忽略) 在解压包解压了程序后,直接编译,出现如下错误. *** WARNING L14: INCOMPATIBLE MEMORY MODEL ...

  10. 达拉草201771010105《面向对象程序设计(java)》第四周学习总结

    实验四类与对象的定义及使用 实验时间 2018-9-20 第一部分:理论知识 1.类与对象概念 (1)类是具有相同属性和方法的一类事物的抽象,是构造对象的模板或蓝图,由类构造对象的过程称为创建类的实例 ...