学习使用 Spring Data JPA 的记录文

概述

本来记了概述的,后来关机丢失了。那就不写了。进入正题。

整体

配置

首先引入pom文件。

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

其后在SpringBoot的启动类之上,使用「@EnableJpaAuditing」注解。

使用

自动创建实体类表

这个功能说实话是我最喜欢的一个hhh,不用自己手动去设计数据库表了,当然,仅限于自己的小项目中使用啦~

在项目的yml加入以下配置。主要是「ddl-auto」这个属性,当我们设置为update之后就会通过实体类自动创建数据表。

spring:
    jpa:
      hibernate:
        ddl-auto: update
      show-sql: false

而我们的实体类如何声明这个表的信息呢?在以下代码中,我们使用「@Entity」和「@Table(name = "sys_user")」两个注解。前者是声明这个是一个实体,然后数据库表的名称为「sys_user」。

@Entity
@Table(name = "sys_user")
public class SysUser 

以下代码中可以看到,我们使用「@Id」注解来声明这个字段是我们数据表中的id,使用「@GenericGenerator」注解声明我们的主键生成策略是「uuid」。之后使用「@Column」进行配置字段相关信息,如「名称」和「长度」。

/**
 * 用户ID
 */
@Id
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(name = "id", length = 32)
private String id;

其他字段就大同小异了。

/**
 * 用户名
 */
@Column(name = "user_name")
private String userName;

自动更新「update_time」与「update_time」

一般的,我们的数据表中都需要加入标题上这两种字段,我们不可能每次赋值的时候都手动的去添加吧。之前在MybatisPlus需要注解+拦截器去实现。而在JPA之中我们可以编写一个类,让别的类去继承它,从而实现这个功能。

如下代码中,主要使用「@EntityListeners(AuditingEntityListener.class)」注解进行拦截数据表的「更新」、「创建」等状态,然后进行重载数据。

然后在之后通过「@CreatedDate」和 「@LastModifiedDate」两个注解对其进行加载数据。

@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
    /**
     * 创建时间
     */
    @JsonIgnore
    @CreatedDate
    @Column(name = "create_time")
    public Date createTime;

    /**
     * 修改时间
     */
    @JsonIgnore
    @LastModifiedDate
    @Column(name = "update_time")
    public Date updateTime;
}

如下类中便直接集成了这个类。

public class SysUser extends BaseEntity{

创建Mapper/自定义接口

在JPA之中,如同之前在使用MybatisPlus的时候一样,我们需要将其继承某个接口,然后它自动去实现这些。像下面代码中我们去继承了「JpaRepository<SysUser,Integer>」类(第二个参数代表它的ID类型),一些比较基础的SQL语句我们都不需要再自己编写。

并且,你也可以通过它的一些规则进行自己定义方法,并且JPA会自动给你去实现这个方法。如同下方代码「findByUsername」就代表它会自动实现一条根据「Username」这条字段为条件查询的SQL。很明显,它的生成策略在于:「findBy」 + 列名。

@Repository
public interface SysUserMapper extends JpaRepository<SysUser,Integer> {
    SysUser findByUsername(String username);
}

相同的,它还提供了一些其他语法,如模糊查询 「findByUserNameLike」、and语句「findByUserNameAndPassword」等等。

最后我们采用「@Repository」去声明这个mapper。

分页

首先需要让mapper继承「PagingAndSortingRepository<实体类, 主键ID类型>」接口。

然后我们就可以直接通过「PageRequest」方法设置分页参数,再之后使用「findAll」方法并以这个分页对象为参数,就可以实现分页操作。

 public Page<SysClassroom> list(int page, int size) {
    PageRequest pageRequest = PageRequest.of(page - 1, size);
    return sysClassroomMapper.findAll(pageRequest);
}

复杂SQL操作

在项目之中我遇到了一个场景——不仅需要分页操作,还需要一些条件!那么使用上述的分页方式就不行了。需要另寻办法。

我们的mapper需要继承于「JpaSpecificationExecutor」接口。

然后我们就需要在对应的service中构造对应的条件,我们需要去new一个名字叫为「Specification」的类,并且实现它的「toPredicate」方法。

    Specification<SysChatMsg> specification1 = new Specification<SysChatMsg>() {

        @Override
        public Predicate toPredicate(Root<SysChatMsg> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
            return null;
        }
    };

那么我们该怎么构建条件呢?我们需要借用类型为「CriteriaBuilder」的参数。用它来编写条件,最终再使用类型为「CriteriaQuery」的参数进行整体的SQL语句拼合。

如以下语句便构建了一个对名为「username」的列名进行了是否相同的对比。另外「criteriaBuilder」还有其他方法,常用的:排序、与、或、非等等。

        Predicate a = criteriaBuilder
                .equal(root.get("username").as(String.class), username);
        Predicate b = criteriaBuilder
                .equal(root.get("password").as(String.class), password);

最后再通过以下代码构建整个语句。

        query.where(criteriaBuilder.or(a, b));
        query.orderBy(criteriaBuilder.asc(root.get("createTime").as(Date.class)));
        return query.getRestriction();

自此,条件便构建完毕了。

接下来我们可以直接通过mapper的「findAll」方法,分别传入条件和分页方法。

        xxxMapper.findAll(specification,pageRequest);