继续创造,加快成长!这是我参加「日新计划 10 月更文应战」的第8天,点击检查活动概况

初识 Spring Data JPA

入职公司(目前已从这家公司离任)后参加的第一个项目,架构师选定的数据库耐久层方案便是 Spring Data JPA。在些之前笔者也是一向运用 MyBatis,未曾听说过 Spring Data JPA。运用 Spring Data JPA 之初也是各种不适应,也曾向架构师提过想换成 MyBatis 的想法,不过架构师一句话就劝服了我:“咱们的项目要兼容多种干流数据库”。后来事实也确实证明运用 Spring Data JPA 是一个很正确的挑选。因为闻名的华为事件,公司领导高枕无忧,要求公司所有项目兼容国产数据库。咱们担任的项目在兼容国产数据库的这件事上没费多大力气,不像公司其它项目费了老鼻子劲。

在这之后就对 Spring Data JPA 颇有好感。

Spring Data JPA 简介

Spring Data JPA 是 Spring Data 的一个子项目,经过供给依据 JPA (Java Persistence API)的 Respository 极大地减少了 JPA 作为数据拜访方案的代码量。经过 Spring Data JPA 框架,开发者能够省略完结耐久层业务逻辑的作业,仅有要做的便是声明耐久层的接口,其他都交给 Spring Data JPA 来完结。

Spring Data JPA最顶层的接口是Repository,该接口是所有Repository类的父类。具体代码如下:

@Indexed
public interface Repository<T, ID> {
}

Repository 接口间的承继联系如下图所示:

Spring Boot | 漫谈 Spring Data JPA

在项目开发中,咱们一般都是完结 JapRepository 类。

集成 Spring Data JPA

1、引进 Maven 依靠

在 Spring Boot 项目中运用 Spring Data JPA,首要需要在 pom.xml 文件中引进依靠。

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

2、在 application.properties 中装备数据源信息及 JPA 属性

# 数据库装备
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
# 装备JPA相关属性
spring.jpa.show-sql=true
# 设置日期类型属性回来格局
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

笔者这里仅仅演示 JPA 的运用,所以挑选的是 H2 内存数据库。假如你运用的是其它类型的数据库,只需要将数据库装备信息改成对应数据库的装备信息就好。

3、界说 POJO 实体类

/**
 * 用户
 */
@Data
@Entity
@Table(name = "t_user")
public class User implements Serializable {
    /**
     * 主键
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    /**
     * 用户编号
     */
    @Column(name = "user_code")
    private String userCode;
    /**
     * 用户名称
     */
    @Column(name = "user_name")
    private String userName;
    /**
     * 创建时刻
     */
    @Column(name = "create_time")
    private Date createTime;
}

实体类运用了@Entity、@Table、@Id等JPA注解修饰。

  • @Entity:每个耐久化 POJO 类都是一个实体 Bean,经过在类的界说中运用 @Entity 注解来进行声明。
  • @Table:声明此目标映射到数据库对应的数据表。
  • @Id:指定表的主键。
  • @GeneratedValue:用于指定主键生成策略。
  • @Column:指定字段在数据表中对应的列名。该注解不是有必要,假如没有,默认运用属性名作为列名。

4、界说 Dao 接口

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    List<User> findByUserName(String userName);
}

这样便具有了体系默认帮咱们完结的办法,包括新增、批量保存、查询一个、查询多个、删除等。

Spring Boot | 漫谈 Spring Data JPA

另外,咱们界说了一个依据用户名查询用户信息的办法。不需要写SQL,也不需要其他代码完结,JPA 会依据办法名主动生成 SQL 来查询数据库。

假如有的办法不想遵从 JPA 的命名规矩,能够随便界说办法名,然后在办法上加上 @Query 注解定制查询数据库的 SQL。

@Query("from User where userCode = ?1")
List<User> findUserByUserCodeTypeA(String userCode);
@Query("from User where userCode = :usercode")
List<User> findUserByUserCodeTypeB(@Param("usercode") String userCode);
@Query(value = "select * from t_user where user_code = :usercode", nativeQuery = true)
List<User> findUserByUserCodeTypeC(@Param("usercode") String userCode);

注意:上面的办法名仅仅便利演示,实践的项目中笔者是不会取这种办法名的。这种类型的办法名能够称得上是代码中的坏滋味了。好的办法名,见名知义。

动态查询

面临比较复杂的查询的时候,上面的两种完结方式就不是那么高雅了。为了处理动态查询的需求,Spring Data JPA 供给了一个 JpaSpecificationExecutor 接口。JpaSpecificationExecutor 接口包括如下办法:

  • long count(@Nullable Specification spec):回来契合条件的记载的数量。
  • List findAll(@Nullable Specification spec):回来所有契合条件的记载。
  • Page findAll(@Nullable Specification spec, Pageable pageable):回来契合条件的记载,Pageable 参数用于操控排序和分页。
  • List findAll(@Nullable Specification spec, Sort sort):回来所有契合条件的记载,Sort 参数用于操控排序。
  • Optional findOne(@Nullable Specification spec):回来一条契合条件的记载,假如契合条件的实体有多个,该办法将会引发异常。

概括地讲,一个 Dao 接口要具有动态查询的能力,就有必要承继 JpaSpecificationExecutor 接口。

Spring Data JPA 支撑动态查询的关键在于 Specification 参数,Specification 用于封装多个代表查询条件的 Predicate 目标。

Specification 接口界说了一个 toPredicate() 办法,该办法回来的 Predicate 目标便是 Criteria 查询的查询条件,程序一般运用 Lambda 表达式完结 toPredicate() 办法来界说动态查询条件。

CriteriaBuilder是 JPA Criteria 查询的中心API,它的效果便是用于生成各种查询条件。

下面咱们经过一个具体的实例来叙述下 JPA 动态查询的运用办法。

修改与 User 实体对应的 Dao 接口,承继 JpaSpecificationExecutor 接口。

@Repository
public interface UserRepository extends JpaRepository<User, Integer>, 
    JpaSpecificationExecutor<User> {
    // ...
}

动态查询用户数据:

List<User> userList = userRepository.findAll(((root, criteriaQuery, criteriaBuilder) -> {
    // 界说集合,用于存放动态查询条件
    List<Predicate> predicateList = Lists.newArrayList();
    predicateList.add(criteriaBuilder.like(root.get("userCode").as(String.class), "%0%"));
    predicateList.add(criteriaBuilder.like(root.get("userName").as(String.class), "%汪%"));
    return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
}));
userList.forEach(System.out::println);

上面的代码,经过 Java 8 的箭头函数,咱们生成了一个包括多个查询条件的 Predicate 目标。当然,上面的代码仅仅为了演示动态查询的功能。实践项目中一般会将生成查询条件的办法封装成工具类。