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. css 控制li点与文字的距离

    ul>li 默认的样式如下 前面的点与文字是有间距的 可见,通过设置 text-indent 属性可以调整li点与文字的距离

  2. MyBatis连接MySQL8配置

    <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</a ...

  3. tar:file-changed-as-we-read-it报错处理

    在使用tar命令对Mysql的数据目录进行备份打包时出现如下报错: tar cvzf mysql.tgz mysql /bin/tar: /path/to/mysql: file changed as ...

  4. pattern space and hold space of sed

    Copied from: stackoverflow When sed reads a file line by line, the line that has been currently read ...

  5. 1、【Spark】Spark安装

    本文基于的环境 Red Hat Linux Enterprise 7 x86_64 jdk 1.7.0_79 Python 2.7Spart spark-1.5.2-bin-hadoop2.6 官方要 ...

  6. Java程序员常用的@Component、@Repository、@Controller、@Service系列【案例demo3】

    Java程序员常用的@Component.@Repository.@Controller.@Service系列[案例demo3]   很多程序员通过在类上使用@Repository.@Componen ...

  7. Cisco模拟器的基本使用

    获取帮助查找命令 只需输入一个'?'便可得到详细的帮助信息,如果想获取c开头的命名,那么直接输入'c?'即可. 在各个模式下切换的方法 给如图所示路由器接口配置IP地址 第一步:安装HWIC-2T(串 ...

  8. 前端性能优化之Lazyload

    前端性能优化之Lazyload @(Mob前端-冬晨)[JavaScript|技术分享|懒加载] [TOC] Lazyload 简介 前端工作中,界面和效果正在变得越来越狂拽炫酷,与此同时性能也是不得 ...

  9. 前端每日实战:55# 视频演示如何用纯 CSS 创作一个太阳、地球、月亮的运转模型

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/RJjQYY 可交互视频 此视频是可 ...

  10. 基于sklearn的metrics库的常用有监督模型评估指标学习

    一.分类评估指标 准确率(最直白的指标)缺点:受采样影响极大,比如100个样本中有99个为正例,所以即使模型很无脑地预测全部样本为正例,依然有99%的正确率适用范围:二分类(准确率):二分类.多分类( ...