Spring Data JPA使用入门

一.Spring Data JPA简介

Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,底层使用了 Hibernate 的 JPA 技术实现,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能接口,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
由于微服务系统的广泛应用,服务粒度逐渐细化,多表关联查询的场景一定程度减少。单表查询和单表的数据操作正是JPA的优势。我们本节就为大家介绍如何在Spring Boot中使用JPA。

二.将Spring Data JPA集成到Spring Boot

2.1 引入maven依赖包,包括Spring Data JPA和Mysql的驱动


    org.springframework.boot
    spring-boot-starter-data-jpa

 
    mysql
    mysql-connector-java

2.2 配置数据库连接和jpa的相关信息

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    hibernate:
      ddl-auto: validate
    database: mysql
    show-sql: true
  • spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect 。Hibernate 创建数据库表的时候,默认使用的数据库存储引擎是 MyISAM ,这个参数作用是在建表的时候,将存储引擎切换为 InnoDB 。
  • spring.jpa.show-sql=true 在日志中打印出执行的 SQL 语句信息。
  • spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置属性,其主要作用是:自动根据实体类的定义创建、更新、验证数据库表结构。所以这个参数是一个比较危险的参数,使用的时候一定要注意。该参数的几种配置如下:
    • create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
    • create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
    • update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
    • validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

三.Spring Data JPA核心用法

3.1 开发实体Model类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name="article")
public class Article {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false,length = 32)
    private String author;

    @Column(nullable = false, unique = true,length = 32)
    private String title;

    @Column(length = 512)
    private String content;

    private Date createTime;
}
  • @Entity 必选注解,表示这个类是一个实体类,接受JPA控制管理,对应数据库中的一个表
  • @Table 可选注解,指定这个类对应数据库中的表名。如果这个类名和数据库表名符合驼峰及下划线规则,可以省略这个注解。如FlowType类名对应表名flow_type。
  • @Id 指定这个字段为表的主键
  • @GeneratedValue(strategy=GenerationType.IDENTITY) 指定主键的生成方式,一般主键为自增的话,就采用GenerationType.IDENTITY的生成方式
  • @Column 注解针对一个字段,对应表中的一列。nullable = false表示数据库字段不能为空, unique = true表示数据库字段不能有重复值,length = 32表示数据库字段最大程度为32.

3.2 开发数据操作接口

我们只需创建类似XxxRepository接口继承JpaRepository即可,JpaRepository为我们提供了各种针对单表的数据操作方法:增删改查、分页、排序等。

public interface ArticleRepository extends JpaRepository {
}

3.3 关键字查询接口

除了JpaRepository为我们提供的增删改查的方法。我们还可以自定义方法,使用起来非常简单,甚至可以说是强大。把下面的方法名放到ArticleRepository 里面,它就自动为我们实现了通过author字段查找article表的所有数据。也就是说,我们使用了find(查找)关键字,JPA就自动将方法名为我们解析成数据库SQL操作,太智能了。

public interface ArticleRepository extends JpaRepository {
    //注意这个方法的名称,jPA会根据方法名自动生成SQL执行
    Article findByAuthor(String author);
}

Article findByAuthor(String author);等同于SELECT * FROM article WHERE author = ?,其他具体的关键字,使用方法和生产成 SQL 如下表所示:

关键字 接口函数例子 JPQL 片段
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age ⇐ ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection age) … where x.age not in ?1
TRUE findByActiveTrue() … where x.active = true
FALSE findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

可以看到我们这里没有任何类SQL语句就完成了两个条件查询方法。这就是Spring-data-jpa的一大特性:通过解析方法名创建查询

四.SpringDataJPA实现分页排序

如果一次性加载成千上万的列表数据,在网页上显示将十分的耗时,用户体验不好。所以处理较大数据查询结果展现的时候,分页查询是必不可少的。分页查询必然伴随着一定的排序规则,否则分页数据的状态很难控制,导致用户可能在不同的页看到同一条数据。本文的主要内容就是给大家介绍一下,如何使用Spring Data JPA进行分页与排序。

4.1 分页参数Page

Pageable是Spring定义的接口,用于分页参数的传递,我们可以使用如下代码进行分页操作:查询第一页(从0开始)的数据,每页10条数据。

Pageable pageable = PageRequest.of(0, 10);   //第一页
//Pageable pageable = PageRequest.of(1, 10);  //第二页
//Pageable pageable = PageRequest.of(2, 10);  // 第三页
//数据库操作获取查询结果
Page
articlePage = articleRepository.findAll(pageable); //将查询结果转换为List List
articleList = articlePage.getContent();

findAll方法以Page类的对象作为响应,如果我们想获取查询结果List,可以使用getContent()方法。但是不建议这样进行转换,因为前端展示一个分页列表,不仅需要数据,而且还需要一些分页信息。如:当前第几页,每页多少条,总共多少页,总共多少条。这些信息在Page(articlePage)对象里面均可以获取到。

4.2 排序参数Sort

Spring Data JPA提供了一个Sort对象,用以提供一种排序机制。

articleRepository.findAll(Sort.by("createTime"));

articleRepository.findAll(Sort.by("author").ascending().and(Sort.by("createTime").descending()));
  • 第一个findAll方法是按照createTime的升序进行排序
  • 第一个findAll方法是按照author的升序排序,再按照createTime的降序进行排序
    分页和排序在一起:
Pageable pageable = PageRequest.of(0, 10,Sort.by("createTime"));

4.3 分页结果Slice与Page

Slice 和Page 都是Spring Data JPA的数据响应接口,其中 Page 是 Slice的子接口,它们都用于保存和返回数据。
Slice的一些重要方法:

public interface Slice extends Streamable {
    List   getContent(); //获取切片的内容
    
    Pageable  getPageable(); //当前切片的分页信息
    
    boolean  hasContent(); //是否有查询结果?
    
    boolean  isFirst();  //是否是第一个切片
    
    boolean  isLast();  //是否是最后一个切片
    
    Pageable nextPageable(); // 下一个切片的分页信息
    
    Pageable previousPageable(); // 上一个切片的分页信息
}

Page的一些重要方法:

public interface Page extends Slice {
    //总页数
    int getTotalPages();
    
    //总数据条数
    long getTotalElements();
}

通过这两个接口的函数定义可以看出,Slice只关心是不是存在下一个分片(分页),不会去数据库count计算总条数、总页数。所以比较适合大数据量列表的的鼠标或手指滑屏操作,不关心总共有多少页,只关心有没有下一页。Page比较适合传统应用中的table开发,需要知道总页数和总条数。

五.测试JPA关键字查询和分页查询

5.1 完善3.3节ArticleRepository接口,增加分页查询:

public interface ArticleRepository extends JpaRepository {
    //注意这个方法的名称,jPA会根据方法名自动生成SQL执行
    Article findByAuthor(String author);
    
    //根据author字段查询article表数据,传入Pageable分页参数,不需要自己写SQL
    Page
findByAuthor(String author, Pageable pageable); }

由于JpaRepository接口继承自PagingAndSortingRepository接口,所以默认已实现分页和排序查询。

5.2编写并运行测试用例

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class JPATest {

    @Resource
    private ArticleRepository articleRepository;
    
    @Test
    public void testFindByAuthor() {
        Article article = articleRepository.findByAuthor("Jerry");
        log.info(article.toString());
    }

    @Test
    public void testPageable() {
        // 查询第一页,每页10条数据,按照createTime字段降序排列
        Pageable pageable = PageRequest.of(0, 10,Sort.by("createTime").descending());
        Page
page = articleRepository.findByAuthor("William", pageable); for (Article article : page.getContent()) { log.info(article.toString()); } } }

你可能感兴趣的:(Spring Data JPA使用入门)