Spring Boot JPA操作数据(六)

简介

在Spring Boot项目开发中,我们操作数据库的时候一般会用到JPA或者是MyBatis框架,今天所要将的是JPA操作数据库案例。后续会说到MyBatis操作数据。大家在开发的时候,看看自己对哪个比较熟练或者是公司项目的需要来选择,上篇我们已经说了Spring Boot如何连接数据库,我们紧跟着上篇Spring Boot 连接数据库(五)的内容来进行操作。

Spring Boot.png

认识JPA

首先我们在遇到一个新的知识的时候,官方技术文档将是我们最好的帮手,或者是网上查询相关技术支持博文。进入JPA官方文档学习吧。

什么是JPA?

Spring Data JPA, part of the larger Spring Data family , makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.
Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed.

翻译如下:

Spring Data JPA是更大的Spring Data系列的一部分,可以轻松实现基于JPA的存储库。 此模块处理对基于JPA的数据访问层的增强支持。 它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。
Spring Data JPA旨在通过减少实际需要的工作量来显着改善数据访问层的实现

JPA有什么特性?

  • 基于Spring和JPA构建存储库的复杂支持
  • 支持Querydsl术语,从而支持类型安全的JPA查询
  • 透明审核域名类
  • 分页支持,动态查询执行,集成自定义数据访问代码的能力
  • 在引用时可通过@Query带注释的自定义查询
  • 支持基于XML的实体映射
  • 基于JavaConfig的存储库配置,介绍@EnableJpaRepositories

配置JPA要求

在Spring Boot中通过JPA来进行操作数据库是非常简便的,并且我们也可以自定义SQL语句,而在满足我们项目的条件下不需要进行书写过多的SQL语句,这能很大程度解决我们的开发效率。

配置

Spring Boot项目是如何进行配置JPA呢?这里我用的是MevanGradle两种方式进行项目的配置来创建Spring Boot项目,但是在本案例开发提供代码是通过Mavan进行的,后期会补充Gradle的案例代码。

Maven配置

1.添加JPA maven依赖库

pom.xml添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<!--JPA依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
  • :这里因为要操作数据,所以需要添加数据库依赖,如MySQL

2.在application.yml配置文件中进行配置(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver # MySql jdbc Driver
# 连接数据库
# eirunye_springboot_notes表示的是你创建的数据库;
# useSSL:是否使用SSL证书验证;
# characterEncoding:编码格式;
# useJDBCCompliantTimezoneShift:是否使用符合JDBC的时区转换;
# useLegacyDatetimeCode:是否使用旧版日期时间码;
# serverTimezone:选择服务器时间方式;
url: jdbc:mysql://127.0.0.1:3306/eirunye_springboot_notes?useSSL=false&requireSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: root #本地设置数据库账号
password: 123456 #密码
jpa:
hibernate:
ddl-auto: update
show-sql: true
server:
port: 8090 #访问端口

  • spring.jpa.hibernate.ddl-auto配置的属性解释:
    • create:每次启动该程序,没有表的会新建表,表内有数据都会清空,结束时,并未清空表
    • create-drop:每次启动、结束程序的时都会清空表,而且启动时会重新创建表,
    • update:每次启动程序时,没有表的会创建表,表不会清空,检查表字段是否相同,不同则更新,
    • validate:每次启动程序时,校验数据与表(字段、类型等)是否相同,不同会报错
    • none:在所有其他情况下,默认为none,这里选择update

application.properties配置文件中进行配置

  • .yml.properties选择其一
1
2
3
4
5
6
7
server.port=8090
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # MySql jdbc Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/eirunye_springboot_notes?useSSL=false&requireSSL=false&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.data-username=root
spring.datasource.data-password=123456
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

Gradle配置

1.在build.gradle添加依赖

1
2
3
4
5
6
7
dependencies {
//注:Android 的Gradle依赖已经改为implementation
compile("org.springframework.boot:spring-boot-starter-data-jpa") //添加jpa依赖
compile ("mysql:mysql-connector-java:5.1.24") //添加MySQL依赖
compile("com.h2database:h2")
testCompile("junit:junit")
}

2.在.yml或者.properties配置文件中进行配置和上面的Maven一样。

JPA操作数据

  • :在这里简单的举例说明JPA的使用,在系列文章会更加深入讲关于JPA的使用,在这里的代码创建到是按照Spring Boot 项目如何搭建(四)项目包层级定义来创建的,所以大家不清楚就再去看看,或者下载本例代码查看就明白了。

1.在.bean包下创建实体User.class

  • :这里添加注解的位置如果选择在属性上面添加的话就全部都选择在属性上面添加,本例的User.class,如果选择在get方法上面添加,那么全部都在get方法上面添加注解,不然有时可能获取不到数据,或者其他错误。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Entity
@Table(name = "user")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

@Column(name = "username",columnDefinition = "VARCHAR(50) NOT NULL COMMENT '用户名不为空!'",unique = true)
@NotBlank(message = "username can't be null")
private String username;

@Column(name = "password",columnDefinition = "VARCHAR(50) NOT NULL COMMENT '密码不为空!'")
@NotBlank(message = "password can't be null")
private String password;

@Temporal(TemporalType.DATE)
@Column(name = "create_time", columnDefinition = "datetime DEFAULT NULL COMMENT '创建时间'")
private Date createTime; //创建时间

@Temporal(TemporalType.DATE)
@Column(name = "update_time", columnDefinition = "datetime DEFAULT NULL COMMENT '更新时间'")
private Date updateTime;//更新时间
// get/set方法 注:Lombok插件可以直接构建get、set方法
}
  • 实体注解解释(这里讲解的是常用的)
注解源 解释说明
@Entity JPA实体映射到数据库
@Table 表示表,@Table(name = "user")表示该实体生成的表是user
@Table还有几个属性,如catalog:对应数据库的catalogschema:对应数据库的schemaUniqueConstraint定义在@Table@SecondaryTable元数据里,用来指定建表时需要建唯一约束的列等
@Id 表示主键Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.AUTO)表示Id自增
@Column name = "username"表示表的字段名为usernamecolumnDefinition表示字段创建的SQL语句说明,第一次创建时生效,unique=true表示该字段的值在表中是唯一的(如:用户名不能相同)
@NotBlank @NotBlank(message = "password can't be null")解释说明
@Temporal @Temporal(TemporalType.DATE)表示时间的定义格式为:yyyy-MM-dd 即:2018-09-01TemporalType.TIME表示为:hh:mm:ss 即 10:12:11TemporalType.TIMESTRAMP表示为:yyyy-mm-dd hh:mm:ss 即:2018-09-01 10:12:11
  • 说明:以上的实体注解解释完成了。当然了,在这里我们使用的JPA的注解只是很少的一部分,接下来的文章会详细说明相关注解的作用和使用。

运行项目生成的表如下

1
2
3
4
5
6
7
8
9
10
11
create table user
(
id int not null primary key,
create_time datetime null comment '创建时间',
password varchar(50) not null comment '密码不为空!',
update_time datetime null comment '更新时间',
username varchar(50) not null comment '用户名不为空!',
constraint UK_sb8bbouer5wak8vyiiy4pf2bx
unique (username)
)
engine = MyISAM charset = utf8;

2.在.controller包下创建UserController.class
这里代码的就是测试增删改查,这里的注解之前的文章都解释过了,这里就不解释了,遇到最新的就解释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@RestController
@RequestMapping("/user")
public class UserController {

@Autowired
UserService userService;

/**
* 通过id查找user信息
* @param id 参数id
* @return 返回Json user
* @throws Exception
*/
@GetMapping(value = "/info/{id}")
public Result<User> getUserDataById(@PathVariable Integer id) throws Exception {

if (null == id) throw new EirunyeException(ResultEnum.UNKNOWN_ERROR);//这里定义自己的提示错误信息,最好每个都有定义这样比较明确错误!!!

return userService.getUserDataById(id);
}
/**
* 插入数据
* @param user 用户信息
* @return
* @throws Exception
*/
@PostMapping(value = "/info/save")
public Result<User> saveUserData(@Valid User user) throws Exception {
if (null == user) throw new EirunyeException(ResultEnum.UNKNOWN_ERROR);//这里定义自己的提示错误信息,最好每个都有定义这样比较明确错误!!!

return userService.saveUserData(user);
}
/**
* 更新user信息
* @param user 参数
* @return 返回提示 更新失败或者成功
* @throws Exception 异常
*/
@PostMapping(value = "/info/update")
public Result<String> updateUserInfo(@Valid User user) throws Exception {

if (null == user) throw new EirunyeException(ResultEnum.UNKNOWN_ERROR);

return userService.updateUserInfo(user);
}
/**
* 通过ID参数user信息
* @param id 参数id
* @return 返回提示 参数成功或者失败
*/
@GetMapping(value = "info/delete/{id}")
public Result<String> deleteUserInfo(@PathVariable Integer id) throws Exception {
if (null == id) throw new EirunyeException(ResultEnum.UNKNOWN_ERROR);

return userService.deleteUserInfo(id);
}
}

3.在.service包下创建UserService.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@Service
public class UserService {

@Autowired
UserRepository userRepository;

/**
* 插入数据
* @param user 用户
* @return Json user
* @throws Exception 异常抛出
*/
public Result<User> saveUserData(User user) throws Exception {

User user1 = userRepository.save(user);
return ResultUtil.getResult(ResultEnum.SUCCESS, user1);
}
/**
* 根据id来获取user信息
* @param id 参数id
* @return Json user
*/
public Result<User> getUserDataById(Integer id) throws Exception {

User user = userRepository.findAllById(id);
if (null == user) {
throw new EirunyeException(ResultEnum.USERNOTEXIST);//用户不存在
} else
return ResultUtil.getResult(ResultEnum.SUCCESS, user);
}
/**
* 更新user信息 更新数据说明:
* 1.如果通过save(S s)方法的话必须带主键Id,这样通过主键id就能更新数据了,
* 如果不带参数id则,数据将会自增一条数据(变成插入数据了)
* 2.自定义SQL语句更新数据(后面会讲到)
*
* @param user 参数user
* @return json String
*/
public Result<String> updateUserInfo(User user) throws Exception {

if (null == user.getId()) return ResultUtil.error(-1, "Id不能为空!");
//更新数据
User userUpdate = userRepository.save(user);
if (userUpdate == null) return ResultUtil.error(-1, "数据更新失败,请联系后台!");

// 一般更新数据步骤:
// 1.通过ID获取当前用户信息
// 2.将所需更新的信息进行设置,如用户名、密码等,而创建时间不需要更新
// 3.如果没有其他特殊字段直接save方法更新(注:这里我就简单直接save了,大家的项目应该是下面这样方式写)
//User userInfo = userRepository.findAllById(user.getId());
//userInfo.setId(user.getId());
//userInfo.setUsername(user.getUsername());
//userInfo.setPassword(user.getPassword());
//userInfo.setUpdateTime(new Date());
//userInfo.setCreateTime(new Date()); //这个不需要设置

return ResultUtil.getResult(ResultEnum.SUCCESS, "更新数据成功!!!");
}

/**
* @param id 参数id
* @return json String
*/
public Result<String> deleteUserInfo(Integer id) throws Exception {

userRepository.deleteById(id);

//下面不需要也可以,直接返回return ResultUtil.getResult(ResultEnum.SUCCESS, "删除数据成功!!!");
User user = userRepository.findAllById(id);
if (null == user) {
throw new EirunyeException(ResultEnum.SUCCESS);
} else return ResultUtil.error(-1, "删除数据失败!!!");
}
}

4.在.repository创建UserRepository接口去扩展JpaRepository<T,ID>

  • 注:JpaRepository<T,ID>一般T是要操作的实体,如本例的User.class,ID是该实体ID类型,如本例定义idInteger,如果你自己定义Long这里就表示Long。在UserRepository接口这里需要注意的是命名规则查找find,删除delete等等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface UserRepository extends JpaRepository<User, Integer> {

/**
* @param id 根据id获取当前user数据
* @return user
*/
User findAllById(Integer id);
/**
* 通过用户名和密码来获取用户
* @param username 参数用户名
* @param password 参数密码
* @return 返回user
*/
User findAllByUsernameAndPassword(String username, String password);
/**
* 模糊查询 自定义查询语句
* @param username
* @return
*/
@Query(value = "select * from user u where u.username like %:username%",nativeQuery=true)
List<User> findAllByUsername(@Param("username") String username);

}
  • 注: JPA的自定义语句都是@Query(value="SQL语句"),下面的方法还是要按照JpaRepository命名规则进行,后面的文章将会说明该内容。

测试

新建单元测试

src\test\java\com\eirunye\spring_boot_jpa\创建UserTest.class,这里只是简单的进行单元测试,之后文章会讲解到Spring Boot项目如何进行单元测试。
测试代码UserTest.class进行查看,这里只贴一些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {

private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);


@Autowired
UserService userService;

@Test
public void getUserInfo() throws Exception {
Result<User> userDataById = userService.getUserDataById(1);
logger.info(userDataById.getData().toString());
System.out.println(userDataById.getData().toString());
}

@Before //@Before是在这个测试类中点击每个@Test都会执行的注解,在`@Test`之前执行
public void saveUserInfo() throws Exception {
User user = new User();
user.setUsername("Eirunye");
user.setPassword("123456");
user.setCreateTime(new Date());//这里只做测试用,一般都是后台处理时间
user.setUpdateTime(new Date());//这里只做测试用,一般都是后台处理时间
System.out.println(user.toString());
Result<User> userResult = userService.saveUserData(user);
System.out.println(userResult);
}
//......
}
  • 测试验证

测试成功.png

  • 测试id输入错误时,如:userService.getUserDataById(0);

id输入错误异常.png

  • 查看数据库

查看数据库.png

IDEA请求数据测试

这里只列举一个,大家进测试就好了。

idea请求数据.png

Postman请求数据测试

这里只列举一个,大家进测试就好了。

Postman请求数据.png

下载

总结

1.本篇文章主要讲解的是JPA与Spring Boot整合操作数据,只是简单的说明JPA的增删改查功能,接下来的文章会深入研究JPA的更多内容,如:JPA的多表操作如何使用、JPA分页操作等等。

2.JPA在项目中使用能使我们快速构建项目,不需要书写过多的SQL语句(相信这是大家都非常喜欢的)

3.前文开始的时候说了JPA一些特性。

4.Android平台上的GreenDao的使用方式和JPA是非常相似的

推荐

如果大家想了解更多的Spring Boot相关博文请进入
我的Spring Boot系列博客栈

继续努力哦!走一个