这里使用 IDEA 来进行工程的建立,使用了 Gradle 对整个项目进行管理,具体的过程以下:java
点击 Create New Project -> Spring Initializr , 以后选择默认的 Initalizr Service URL,而后填写项目的信息,以下所示:mysql
这里采用了 Gradle 来进行项目的管理,而后就是对 SpringBoot 进行选择,以下:web
依赖组件 | 含义 |
---|---|
Spring Boot DevTools | 热部署插件,能够在项目运行时自动替换 class,生产环境禁用 |
Lombok | 简化 Java 代码 |
Spring Web Starter | Spring Web 开发的起步依赖 |
Spring Data JPA | 持久层 ORM 框架 |
MySQL Driver | MySQL 驱动 |
作完这一步以后就是选择项目的存储位置。所有完成以后项目的结构以下所示:spring
../blog/
├── blog.iml
├── build.gradle
├── .gradle
└── src
├── main
│ ├── java
│ └── resources
└── test
└── java
复制代码
这里主要是对 Gradle 的配置作出小小的改动,在 repositories
节点下增长新的仓库配置,具体以下:sql
plugins {
id 'org.springframework.boot' version '2.1.6.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'hk.mars'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
maven { url "https://maven.aliyun.com/repository/spring-plugin" }
maven { url "https://maven.aliyun.com/repository/spring" }
maven { url "https://repo.spring.io/libs-release" }
maven { url "https://repo.spring.io/milestone" }
mavenLocal()
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
复制代码
这里须要注意的地方是 Gradle 在初始化的时候速度略慢,甚至可能会失败,主要是 Gradle 在初始化时会下载一个 .zip 文件,受到国内网络大环境的影响,该网站响应较慢,不过对此网络上有不少解决的办法。shell
同时国内访问 Maven 中央仓库也受到网络大环境的影响比较慢,这里设置了阿里云提供的 Maven 仓库,能够提升访问速度(位置靠上的仓库优先访问)。数据库
SpringData JPA 能够在项目启动的时候会自动的数据库中的表,所以没必要提早初始化数据库,只需在配置文件中配置数据源便可,固然这种自动生成表的方法是不容许在生产环境中使用的,具体的配置以下:json
application.propertiesapi
spring.datasource.url=jdbc:mysql://x.x.x.x:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=xxxxxx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
复制代码
这里须要进一步说明的是 spring.jpa.properties.hibernate.hbm2ddl.auto
的含义:网络
前文提到过 SpringBoot JPA 在项目启动的时候会自动的在数据库中生成相应的表,该项配置就是设置自动生成的策略,一个有四个选项,分别以下:
生成策略 含义 create 每次加载 hibernate 时都会从新生成表 create-drop 每次加载 hibernate 时都会从新生成表,在 sessionFactory 关闭时删除表结构 update 第一次加载 hibernate 时都会从新生成表,以后每次加载 hibernate 会更新表结构 validate 验证模型与数据库表结构是否匹配 none 若是不配置该项,默认不对数据库作任何改动
在设计数据库的时候,不一样的表可能具备相同的字段,用来存储一些通用的信息,例如每一张表中都会有 id
字段做为主键,同时存在 create_time
、 update_time
用来存储表中每一行数据的通用信息,这时候就须要定义一个超类来表示这些信息。 这一功能可使用 @MappedSuperclass
注解实现,具体实现以下的 BaseEntity.java :
package hk.mars.user;
import lombok.Data;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.sql.Timestamp;
/** * @author qingke.hk@gmmail.com */
@MappedSuperclass
@Data
public abstract class BaseEntity {
@Id
@GeneratedValue(generator = "id-uuid")
@GenericGenerator(name = "id-uuid", strategy = "uuid")
private String id;
@Column(name = "create_time")
private Timestamp createTime;
@Column(name = "update_time")
private Timestamp updateTime;
}
复制代码
@Data
注解是 Lombok 提供的注解,能够简化 Java 代码。
@Id
注解通常是用在属性上,表示该属性对应的数据库表字段为主键类型,同时可使用 @GeneratedValue
和 @GenericGenerator
指定主键生成策略,在上文使用了内置的 uuid
的生成策略,但 hibernate 还提供了替他的策略,同时还支持自定义的主键生成策略。
@MappedSuperclass
注解是 SpringData JPA 提供的注解,主要是用于用于类上,表示该类是全部的 Entity 的父类,固然你要在你的 Entity 中显示的继承,具体的用法能够继续向下看。
- todo: 写一篇 hibernate 的主键生成策略相关的博客
- todo: 写一篇 Lombok 的博客
首先定义用户角色的枚举:
package hk.mars.user;
import lombok.Getter;
/** * @author qingke.hk@gmmail.com */
public enum UserRole {
/** 管理员角色 */
ADMIN("admin"),
/** 用户角色 */
USER("user");
@Getter
private String role;
UserRole(String role) {
this.role = role;
}
}
复制代码
@Getter
是有 Lombok 提供的,能够在编译的时候自动生成 Getter 方法。
接下来咱们将在继承 BaseEntity
的基础之上定义用户模型,以下 UserEntity.java 所示:
package hk.mars.commons;
import hk.mars.commons.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import java.util.Date;
/** * @author qingke.hk@gmmail.com */
@Data
@Entity
@Table(name = "user")
@EqualsAndHashCode(callSuper = false)
@SuppressWarnings("WeakerAccess")
public class UserEntity extends BaseEntity {
private String name;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@DateTimeFormat(pattern = "yyyy-mm-dd")
private Date birthday;
private String email;
private String password;
@Enumerated(EnumType.STRING)
private UserRole role;
}
复制代码
@Data
与 @EqualsAndHashCode
注解是有 Lombok 提供,用来简化 Java 代码。
@Entity
注解是最主要的一个注解,表示这个类表示数据库中的一个表。
@Table
注解主要是声明表的配置,例如上文能够设置表名,固然还能够设置 数据库的名称、设置索引等等。
@Column
注解主要是对数据库表字段与 Java 类属性之间的关系进行配置,上文仅仅是设置了字段名称,同时还能够设施是否惟1、长度限制、是否可控等等。
@Enumerated
表示属性是一个注解类型,可是在存储的时候字符串形式进行存储。
@DateTimeFormat
注解是有 Spring 提供的,在这里由于 birthday
是时间类型,而调用 RESTful 接口的时候是以 JSON 形式,若是不走任何处理是没法将 JSON 字符串转化为时间类型的,所以咱们在这里使用了 @DataTimeFormat
注解。
- todo: 后面写一些关于 SpringData JPA 的更多的用法。
SpringBoot JPA 能够将接口中的方法名解析为相应的 SQL 语句,但同时针对一些常见的 CRUD 操做提供了一个 CrudRepository
的接口,经过继承能够实现一些常见的 CRUD 操做,固然若是 CrudRepository
提供的操做不能知足业务的需求能够编写新的接口,以下,定义了一个新的分页查询的接口:
UserRepository.java
package hk.mars.user;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
/** * @author qingke.hk@gmmail.com */
public interface UserRepository extends CrudRepository<UserEntity, String> {
/** * 查询全部的用户信息 - 分页 * * @param pageConf 分页参数 * @return 用户信息 */
Page<UserEntity> findAll(Pageable pageConf);
}
复制代码
当你已经完成上述的操做后,此时的项目的结构以下所示:
../blog/
├── blog.iml
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── hk.mars
│ │ ├── Application.java
│ │ ├── commons
│ │ │ ├── BaseEntity.java
│ │ │ └── response
│ │ │ ├── ResultResponse.java
│ │ │ └── ResultResponseBuilder.java
│ │ ├── config
│ │ └── user
│ │ ├── UserController.java
│ │ ├── UserEntity.java
│ │ ├── UserRepository.java
│ │ ├── UserRole.java
│ │ └── UserService.java
│ └── resources
│ └── application.properties
└── test
└── java
└── hk
└── mars
复制代码
固然此时的项目代码多了一些别的东西,不过都与 SpringData JPA 无关,在下载源码以后你们能够自行查看便可。
接下来就能够编写 Service 和 Controller 来对以前的代码进行测试:
UserService.java:
package hk.mars.user;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.List;
/** * @author qingke.hk@gmmail.com */
@SuppressWarnings("WeakerAccess")
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<UserEntity> getUserList(Pageable pageConf) {
Page<UserEntity> userInPage = userRepository.findAll(pageConf);
return userInPage.getContent();
}
public void saveUser(UserEntity userEntity) {
String password = userEntity.getPassword();
userEntity.setPassword(DigestUtils.md5DigestAsHex(password.getBytes()));
this.userRepository.save(userEntity);
}
}
复制代码
UserController.java:
package hk.mars.user;
import hk.mars.commons.response.ResultResponse;
import hk.mars.commons.response.ResultResponseBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/** * @author qingke.hk@gmmail.com */
@RestController
public class UserController {
private static final int DEFAULT_PAGE_NUMS = 10;
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/user")
public ResultResponse<List<UserEntity>> getUsers(
@RequestParam(value = "page-num", defaultValue = "0") Integer pageNum,
@RequestParam(value = "page-size", defaultValue = "10") Integer pageSize) {
Pageable pageRequest = PageRequest.of(pageNum, pageSize == null ? DEFAULT_PAGE_NUMS : pageSize);
List<UserEntity> userList = this.userService.getUserList(pageRequest);
return ResultResponseBuilder.buildSuccess(userList);
}
@PostMapping("/user")
public ResultResponse saveUser(UserEntity user) {
if (user == null) {
return ResultResponseBuilder.buildError("use information is null");
}
this.userService.saveUser(user);
return ResultResponseBuilder.buildSuccess();
}
}
复制代码
而后就是启动项目,开始测试:
同时在启动的过程当中经过观察日志输出能够看到 SpringData JPA 会在项目启动的过程当中删除掉数据库的旧表,而后从新建表:
对于 RESTful 风格的接口能够经过 IDEA 自带的功能进行测试,能够新建一个 rest-api.http 文件,经过该文件就能够直接在 IDEA 中进行测试,文件内容以下:
POST http://localhost:8080/user?name=Tom&password=123456&birthday=2019-07-11&email=test@test.test
Accept: */*
Cache-Control: no-cache
###
GET http://localhost:8080/user?page-num=0&page-size=10
Accept: */*
Cache-Control: no-cache
复制代码
经过点击左侧边栏上的三角标志就能够启动一次测试,同时 IDEA 会将测试结果保存为一个 JSON 文件,这样就能够查看测试的历史,查看的方法是在下图的 2019-07-14T125418.200.json
使用快捷键 Command + B 就能够直接转跳到文件中去。
点击测试以后会显示以下,能够看到测试的返回结果是成功了,而后咱们去查看数据库中的信息,能够看到数据库中已经插入了一条数据:
而后运行第二个测试,看分页查询是否成功:
至此本片博客就基本上完成了,咱们已经能够简单的使用 SpringData JPA 进行操做。
- 上传项目至 Github