4.3 使用JPA操作数据库

4.3.1 JPA介绍

JPA是Java Persistence API的简称,是JCP组织发布的Java EE标准之一。JPA是一种面向对象的查询语言,定义了独特的JPQL(Java Persistence Query Language),是一种针对实体的查询语言,无论是查询还是修改,全部操作的都是对象实体,而非数据库的表。

4.3.2 JPA依赖配置

新建项目,在pom文件中加入JPA依赖、MySQL依赖以及Web功能依赖,如代码清单4-30所示。

4.3.3 配置文件

在配置文件中加入数据库配置,与4.1节介绍的一致。接下来进行JPA的基本配置,比如spring.jpa.hibernate.ddl-auto。其提供了如下几种配置。

• validate:在加载hibernate时,验证创建数据库表结构。

• create:每次加载hibernate,重新创建数据库表结构,设置时要注意,如果设置错误的话,就会造成数据的丢失。

• create-drop:在加载的时候创建表,在关闭项目时删除表结构。

• update:加载时更新表结构。

• none:加载时不做任何操作。

根据具体情况选择配置即可。另外,如果需要,我们也可以加入spring.jpa.show-sql配置,设置为true时,可以在控制台打印SQL。

案例配置代码如代码清单4-31所示。

4.3.4 创建实体对象

创建一个实体对象,在类上加入注解@Entity来表明这是一个实体类,在属性上使用@Id表明这是数据库中的主键ID,使用@GeneratedValue(strategy = GenerationType.IDENTITY)表明此字段自增长,在属性上加入@Column(nullable = false,unique = true)可以设置字段的一些属性,比如nullable为非空、unique唯一约束,还提供了其他属性,这里就不一一介绍了。User实体类代码如代码清单4-32所示。

4.3.5 创建数据操作层

新建一个repository接口,使其继承JpaRepository,这个接口默认提供一组与JPA规范相关的方法,其源代码如代码清单4-33所示。

从源代码中可以看到,默认为我们提供了很多简单的方法,如findAll()、getOne()等,而JpaRepository则继承了PagingAndSortingRepository接口。PagingAndSortingRepository接口代码如代码清单4-34所示。

PagingAndSortingRepository接口继承了CrudRepository接口,实现了有关分页排序等相关的方法,其代码如代码清单4-35所示。

CrudRepository接口继承了Spring Data JPA的核心接口Repository,实现了有关CRUD相关的方法(增、删、改、查)。在Repository接口中没有提供任何方法,仅仅作为一个标识来让其他类实现它作为仓库接口类,其代码如代码清单4-36所示。

代码清单4-36 JPA项目Repository类代码

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

细心的读者可以看到,除了Repository接口以外,其余接口都含有一个@NoRepositoryBean注解,加入这个注解的类,Spring就不会实例化,用作父类的Repository。

4.3.6 简单测试运行

新建一个Controller进行测试,创建一个UserController,在控制层注入刚刚创建的UserRepository,并在控制层创建CURD的4个方法,代码如代码清单4-37所示。

从上面的代码可以看到,在使用JPA操作数据库时,操作特别简单,基本上使用Repository提供的几个方法已经可以满足我们的需求。

4.3.7 JPA扩展学习

前面学习了JPA的简单使用,接下来我们对它进行扩展学习。在JPA使用中,可以通过一些特定的命名规则实现对SQL语句条件的改变。

假设有一个User表,内含字段id(int)、user_name(varchar[50])、pass_word(varchar[50])、age(int)、birthday(date)、is_enable(tinyint[1])。表4-1显示了通过特定命名规则对SQL语句的改变。

表4-1 通过特定命名规则对表User进行的SQL语言的运用

上述方法都是基于既定的接口规则,其实JPA是支持注解形式执行SQL语句操作的,比如在接口上使用@Query,在内容中放入需要执行的SQL语句,如代码清单4-38所示。

代码清单4-38 JPA项目findAllByUserName方法代码

    @Query("SELECT u FROM User u WHERE user_name = :userName")
    User findAllByUserName(@Param("userName") String userName);

需要注意的是,这里是以对象为单位查询的,比如上面使用的是查询的SELECT u,而不是我们在SQL内写的SELECT *,使用@Param给参数取别名,方便在SQL内使用。JPA的使用就扩展到这里,其用法还有很多,感兴趣的读者可以参考官方文档:https://docs.spring.io/spring-data/data-jpa/docs/current/api/。

4.3.8 基于WebFlux的使用

之前我们提到过Spring Boot 2.X版本新提供的WebFlux,接下来改用WebFlux操作JPA。WebFlux暂时还不支持关系型数据库,所以本小节的数据库改用MongoDB,介绍响应式编程操作数据库。

1. 更换为WebFlux依赖

新建一个项目,在pom文件中加入spring-boot-starter-data-mongodb-reactive和spring-boot-starter-webflux依赖。完整pom文件依赖代码内容如代码清单4-39所示。

在配置文件中配置MongoDB数据库信息,配置内容如代码清单4-40所示。

代码清单4-40 JPA项目基于WebFlux配置文件代码

    ##mongo配置
    spring.data.mongodb.host=127.0.0.1
    spring.data.mongodb.port=27017
    spring.data.mongodb.database=test

创建一个实体类,实体类内容如代码清单4-41所示。

创建数据操作层UserRepository,在使用WebFlux时,我们需要继承ReactiveMongoRepository,类代码内容如代码清单4-42所示。

在第3章我们学习WebFlux响应式编程的时候了解到,需要创建一个Handler用于实现具体方法,再创建一个Router用于路由跳转。首先,创建一个UserHandler,我们先写一个保存用户的方法,在WebFlux中可以利用下面的方法直接获取Post请求对象体(Requestbody)中的对象,如代码清单4-43所示。

代码清单4-43 JPA项目基于WebFlux依赖bodyToMono的使用代码

    Mono<UserInfo> user = request.bodyToMono(UserInfo.class);

其中,request是ServerRequest对象,通过上面的方法可以直接将Post请求中的方法体(Requestbody)数据转换为UserInfo对象。我们来完善一下保存方法,方法内容如代码清单4-44所示。

在方法返回值中使用ServerResponse.ok()方法表明返回状态成功,在类似插入、修改等不需要返回值的方法后面加入then()方法返回一个Mono<Void>。

修改方法与插入方法类似,这里不再赘述。接下来我们来看一个查询方法,比如想获取ID为1的用户,就可以利用request.pathVariable方法获取路径中的参数,如代码清单4-45所示。

代码清单4-45 JPA项目基于WebFlux依赖获取链接参数代码

    Long userId = Long.valueOf(request.pathVariable("id"));

我们可以清楚地看到,上述代码就是从路径中获取id的Long值。完善一下方法,将查询到的用户返回,方法内容如代码清单4-46所示。

与插入方法不同的是,在返回值上表明了contentType,这里设置成了application/json,并且利用body方法将查询内容返回。

这里还列举了删除方法和查询列表方法的内容,和前面介绍的方法类似,可以参考使用。UserHandler类如代码清单4-47所示。

创建一个UserRouter作为路由,在其中配置UserHandler类内方法对应路由,如代码清单4-48所示。

关于测试这里就不继续介绍了,以上方法都是笔者亲测可用的,感兴趣的读者可以自行测试。