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的驱动

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
 <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

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<T,ID>即可,JpaRepository为我们提供了各种针对单表的数据操作方法:增删改查、分页、排序等。

public interface ArticleRepository extends JpaRepository<Article,Long> {
}

3.3 关键字查询接口

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

public interface ArticleRepository extends JpaRepository<Article,Long> {
    //注意这个方法的名称,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<Article> articlePage = articleRepository.findAll(pageable);
//将查询结果转换为List
List<Article> 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<T> extends Streamable<T> {
    List <T>  getContent(); //获取切片的内容
    
    Pageable  getPageable(); //当前切片的分页信息
    
    boolean  hasContent(); //是否有查询结果?
    
    boolean  isFirst();  //是否是第一个切片
    
    boolean  isLast();  //是否是最后一个切片
    
    Pageable nextPageable(); // 下一个切片的分页信息
    
    Pageable previousPageable(); // 上一个切片的分页信息
}

Page的一些重要方法:

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

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

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

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

public interface ArticleRepository extends JpaRepository<Article,Long> {
    //注意这个方法的名称,jPA会根据方法名自动生成SQL执行
    Article findByAuthor(String author);
    
    //根据author字段查询article表数据,传入Pageable分页参数,不需要自己写SQL
    Page<Article> 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<Article> page = articleRepository.findByAuthor("William", pageable);
        for (Article article : page.getContent()) {
            log.info(article.toString());
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容