springboot如何整合spring-data-jpa进行项目开发以及如何正确使用spring-data-jpa?
- 发表于:2022-10-10 13:55
- 阅读(375)
(1)Spring Data JPA 简介
Spring Data JPA 是Spring Data家族的一个模块,使用Spring Data JPA我们可以很容易地访问和操作数据库数据。
不过我们要区分JPA和Spring Data JPA他们之间的关系,JPA是一套规范,由于目前ORM框架比较多,并且参差不齐,各自有各自的标准,所以Sun公司就统一了一套ORM的规范,这套规范就是JPA规范,而Spring Data JPA只是这套JPA规范的其中一个实现,另外还有Hibernate等框架也是这套JPA的具体实现框架。
把ORM统一一套JPA规范的好处就是方便后续进行ORM框架的切换。
(2)SpringBoot 整合 JPA
下面我们将简单通过一个示例项目来看看这个spring data jpa应该如何使用。注意:我这里使用的是 mysql 数据库。
1、首先我们需要创建一个空的springboot项目,然后在 pom.xml 导入相关依赖,pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-data-jpa-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-data-jpa-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.3</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
<!-- 核心依赖就是这个 spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--引入Knife4j的官方start包,Swagger2基于Springfox2.10.5项目-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<!--使用Swagger2-->
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.example.SpringDataJpaDemoApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
spring-data-jpa核心依赖就是这个:
<!-- spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
为了后续调试方便,我又添加了swagger依赖,就是这个:knife4j-spring-boot-starter,springboot整合knife4j也挺简单,这里就不重点说明有关knife4j的配置了。
2、修改 application.yml 配置,如下:
# 应用服务 WEB 访问端口
server:
port: 8080
# 应用名称
spring:
application:
name: spring-data-jpa-demo
# mysql 数据源配置
datasource:
druid:
url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&useSSL=false&characterEncoding=utf8
username: demo
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# knife4j 如果开发者使用的是Knife4j 2.x版本,并且Spring Boot版本高于2.4,那么需要在Spring Boot的yml文件中做如下配置:
mvc:
pathmatch:
# 配置策略
matching-strategy: ant_path_matcher
# spring-data-jpa 核心配置:
jpa:
# 默认是none,默认不会自动建表,这里设置为update,则会自动更新数据库中的表字段,
# 如果表不存在,spring-data-jpa也会根据实体类定义的属性自动创建表
hibernate:
ddl-auto: update
# 默认是false,默认不会在控制台显示执行的sql语句,这里设置为true,在控制台就会显示sql语句了
show-sql: true
spring-data-jpa 核心配置主要是这个:
spring:
# spring-data-jpa 核心配置:
jpa:
# 默认是none,默认不会自动建表,这里设置为update,则会自动更新数据库中的表字段,
# 如果表不存在,spring-data-jpa也会根据实体类定义的属性自动创建表
hibernate:
ddl-auto: update
# 默认是false,默认不会在控制台显示执行的sql语句,这里设置为true,在控制台就会显示sql语句了
show-sql: true
OK,到这里我们 springboot 整合 spring-data-jpa 就算整合完成了。总结起来就是在 pom.xml 加个依赖,然后在 application.yml 中配置一下,就可以了。
在设计MVC项目结构的时候,我们可以把实体类放在 entity 包(也可以是 bean 或 pojo)下,注意访问数据库(也就是传统的 dao 层)我们就应该要定义成 repository 了,基于jpa的命名习惯,而且我们每一个接口都需要继承 JpaRepository 的,所以还是命名为 repository 比较合适一点,就像我们以往使用 mybatis 的时候,我们更习惯于把 dao 定义成 mapper 一样。
(3)基本用法
我们为了验证这个配置:ddl-auto: update
,我们可以在代码中新建一个用户服务,一方面我们可以看看数据库会不会自动生成表结构,另一方面再看看 spring-data-jpa 最基本的使用示例。
1、定义实体类 User ,并配置好自动建表字段定义:
package com.example.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 用户
*
* @author guppies
* @since 2022-07-17
*/
@Data
@Accessors(chain = true)
//@Entity:指定当前类是实体类
@Entity
//@Table:指定实体类和表之间的对应关系
@Table(name = "sys_user")
//表名注释需要使用 @org.hibernate.annotations.Table 注解,上面的注解无法指定表名注释
@org.hibernate.annotations.Table(appliesTo = "sys_user", comment = "系统用户")
@NoArgsConstructor
@AllArgsConstructor
public class User {
//@Id:指定当前字段是主键
@Id
@Column(name = "id", columnDefinition = "bigint(20) comment 'ID'")
//@GeneratedValue:指定主键的生成方式 strategy-指定主键生成策略, generator-选择主键别名
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//@Column:指定实体类属性和数据库表之间的对应关系
//name:指定数据库表的字段名称
//unique:是否唯一
//nullable:是否可以为null
//insertable:是否可以插入
//updatable:是否可以更新
//columnDefinition: 定义建表时创建此列的DDL
@Column(name = "user_name", nullable = false, columnDefinition = "varchar(16) default '' comment '用户名称'")
private String userName;
//这里不指定字段名,spring data jpa也会对驼峰的属性名正确转成对应的字段名
@Column(nullable = false, columnDefinition = "varchar(16) default '' comment '用户年龄'")
private String userAge;
@CreatedDate
@Column(name = "create_time", nullable = false, columnDefinition = "datetime comment '创建时间'")
//@JsonFormat:插入/修改/读取的时间转换成想要的格式 pattern-展示格式 timezone-国际时间
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createTime;
@LastModifiedDate
@Column(name = "update_time", nullable = false, columnDefinition = "datetime comment '更新时间'")
//@JsonFormat:插入/修改/读取的时间转换成想要的格式 pattern-展示格式 timezone-国际时间
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime updateTime;
}
顺便把 User 实体类对应的dao接口: UserRepository ,也定义一下,如下:
2、定义dao接口 UserRepository 实现对数据库的操作:
package com.example.repository;
import com.example.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
/**
* <p>
* 用户 Repository 接口
* </p>
*
* @author guppies
* @since 2022-10-10
*/
//可以看到,这个接口继承了JpaRepository<实体,ID>,spring-data-jpa只需要这个信息,就可以帮你完成常用的操作:增删查改
//public interface UserRepository extends CrudRepository<User, Long> {
public interface UserRepository extends JpaRepository<User, Long> {
}
spring-data-jpa 最方便的就是只要继承这个 JpaRepository 接口,我们的UserRepository接口就拥有了对实体表基本的操作,也就是最基本的 增、删、改、查。
3、定义controller类 UserController 来简单测试一下jpa的增删改查,这里为了省时间省去了 service 的定义,service 业务逻辑层在正式的项目开发中是必须要有的,所有和业务逻辑有关的代码都应该写在service中,controller只需要写对service的调用即可,这里为了方便演示先暂时写在controller:
package com.example.controller;
import com.example.entity.User;
import com.example.repository.UserRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author guppies
* @since 2022-07-17
*/
@Api(tags = "用户模块")
@RestController
@AllArgsConstructor
@RequestMapping("/user")
public class UserController {
private final UserRepository userRepository;
//POST(CREATE):在服务器新建一个资源
@ApiOperation(value = "新增用户", notes = "新增用户信息")
@PostMapping("")
public User create(@ApiParam(value = "用户名称", required = true) @RequestParam(value = "userName") String userName,
@ApiParam(value = "用户性别", required = true) @RequestParam(value = "userAge") String userAge) {
//插入一条表记录,ID为null,使用数据库自增的ID。
User user = new User();
user.setUserName(userName);
user.setUserAge(userAge);
LocalDateTime now = LocalDateTime.now();
user.setCreateTime(now);
user.setUpdateTime(now);
return userRepository.save(user);
}
//PUT(UPDATE):在服务器更新完整资源(客户端提供改变后的完整资源)
//PATCH(UPDATE):在服务器更新部分资源(客户端提供改变的属性)
@ApiOperation(value = "更新用户", notes = "更新用户信息")
@PutMapping("/{userId}")
public User update(@ApiParam(value = "用户ID", required = true) @PathVariable("userId") Long userId,
@ApiParam(value = "用户名称", required = true) @RequestParam(value = "userName") String userName,
@ApiParam(value = "用户性别", required = true) @RequestParam(value = "userAge") String userAge) {
//根据主键ID获取一条表记录,然后更新这条表记录的相关信息,注意:更新这里和插入一样都是使用 save 方法
//save方法会自动判断实体类的ID是否有值,如果是null,save就是插入,如果不为null,那么save就是更新
User user = userRepository.findById(userId).orElseThrow(() -> new NullPointerException("用户信息不存在"));
user.setUserName(userName);
user.setUserAge(userAge);
user.setUpdateTime(LocalDateTime.now());
//修改的时候需要全部实体数据,因为jpa的save()是全部修改,前端少传一个字段,数据库更新可能就变成null了,特别注意
return userRepository.save(user);
}
//GET(SELECT):从服务器取出资源(一项或多项)
@ApiOperation(value = "获取用户详情", notes = "获取用户详情")
@GetMapping("/{userId}")
public User info(@ApiParam(value = "用户ID", required = true) @PathVariable("userId") Long userId) {
//根据主键ID获取一条表记录,注意:findById 返回的对象是Optional类,这里只需要使用 orElse 或 orElseThrow 获取到数据即可
//有关Optional类 orElse 或 orElseThrow 的用法请自行百度,其实就是判断如果表记录不存在时应该做什么处理,是直接返回null,还是抛出自定义异常
return userRepository.findById(userId).orElseThrow(() -> new NullPointerException("用户信息不存在"));
}
//DELETE(DELETE):从服务器删除资源
@ApiOperation(value = "删除用户", notes = "删除用户信息")
@DeleteMapping("/{userId}")
public Boolean delete(@ApiParam(value = "用户ID", required = true) @PathVariable("userId") Long userId) {
//根据主键ID删除一条表记录
userRepository.deleteById(userId);
return true;
}
//GET(SELECT):从服务器取出资源(一项或多项)
@ApiOperation(value = "获取用户列表", notes = "获取用户列表")
@GetMapping("")
public List<User> list() {
//查询出用户表中所有表记录
return userRepository.findAll();
}
}
然后我们启动项目,观察数据库中是否会自动创建 sys_user 这张表,可以看到启动的时候有打印出日志,执行了建表的sql,如下图:
建表sql:
create table sys_user (id bigint(20) comment 'ID' not null auto_increment, create_time datetime comment '创建时间' not null, update_time datetime comment '更新时间' not null, user_age varchar(16) default '' comment '用户年龄' not null, user_name varchar(16) default '' comment '用户名称' not null, primary key (id)) comment='系统用户' engine=InnoDB
使用数据库客户端工具查看实际的表结构,如下图所示,大体上是符合我们要求的:
然后我们再使用 swagger 去测试每个接口,我本地测试,增删改查是没问题的。
(4)更多用法
(5)常见问题
(6)总结
参考:
Accessing Data with JPA
Querydsl
Accessing data with MySQL
解决IDEA创建SpringBoot项目时初始化失败的问题
Spring boot 搭配 JPA 生成表注释 和 字段注释
最详细的Spring-data-jpa入门(一)
最详细的spring-data-jpa入门(二)
相关推荐
-
mac安装subversion,并使用svn命令检出服务器上的代码库项目
mac安装svn只要通过Homebrew安装即可,不需要下载额外的安装包手动安装,Homebrew类似一个软件库,我们可以通过brew命令实现一键下载并安装我们所需要的常用软件。
-
Java如何获取泛型类T的Class
我们平时在封装接口或抽象类的时候经常会用到Java的泛型,经常会在传入一个泛型类T,然后封装一些抽象的方法,泛型的好处就是在编译的时候检查类型安全,并且所有的强制类型转换都是隐式和自动的,这样可以提高代码的通用性。但是我们有时候需要获取泛型类的Class,那可以如何获取到呢?
-
springboot项目事务报错:Transaction synchronization is not active
这几天在使用spring声明式事务的时候突然报了一个错误:Transaction synchronization is not active,之前使用的都是好好的,为什么这次就不行了呢?不就是加一个 @Transactional 而已嘛???
-
mysql应该如何在where语句中添加if语句进行条件判断?where if 语句应该如何使用
我们在平时的项目开发中,有时候会遇到复杂一点的需求,需要我们手动编写复杂的SQL语句,并且有时候需要根据每条表记录的实际情况进行判断,根据每条记录动态添加不同的where条件,这个时候我们就可以在where语句中使用if语句进行条件判断,那么where if应该如何正确使用呢?
-
关于websocket多节点分布式问题的解决方案
websocket是一种在单个TCP连接上进行双全工通信的协议,使用websocket,我们可以实现服务端主动向各个订阅消息通道的客户端推送消息。这点比传统的http轮询请求要更好一点,避免一些无用的请求,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
-
mac如何把文件压缩成tar、zip以及如何解压tar、zip?
有时候我们需要把文件压缩成一个tar文件或zip文件,发送给别人,那么在macos系统应该如何压缩和解压缩呢?
-
openjdk和jdk有什么区别,应该如何选择?
我们一开始学习java的时候,安装的都是从sun官网或oracle官网下载的jdk安装包,但其实还有另外一个来源可以获取到jdk安装包,那就是openjdk,它和jdk基本一样,推荐使用openjdk。
-
如果git仓库发生变更,IDEA如何直接修改git远程仓库地址?
有时候我们整理远程仓库代码的时候,会修改远程仓库的名称,或者所属分组,这个时候在IDEA由于还是使用原先拉取的旧仓库地址,导致本地代码会提交不了,也更新不了远程最新代码,那么这个时候要如何修改IDEA当前的git远程仓库地址呢?如何无缝修改,修改完之后就能和原来一样更新提交,并且以前的提交记录也保留呢?
-
微信公众号自定义菜单报错:no permission to use weapp in menu rid:xxxxxxx
昨晚公司系统添加微信公众号菜单突然报错:{"errcode":45064,"errmsg":"no permission to use weapp in menu rid: 60311f70-0736ff08-29143906"}
-
Java如何使用stream流对List列表数据进行自定义排序
我们一般做排序功能都是通过在mysql数据库中的表中定义好排序字段,然后使用升序或降序来进行排序,复杂一点的话就配合多个排序字段进行排序,但是如果碰到那种无法使用表的字段进行排序的情况,我们需要先从数据库中取出列表数据,然后再通过业务代码对列表进行排序,这个时候我们就可以使用redis或Java的stream流。
-
微信企业付款到零钱报错:此请求可能存在风险,已被微信拦截
具体错误信息:com.github.binarywang.wxpay.exception.WxPayException: 返回代码:[SUCCESS],返回信息:[支付失败],结果代码:[FAIL],错误代码:[NO_AUTH],错误详情:[此请求可能存在风险,已被微信拦截。]
-
springboot项目使用@Transactional注解如何避免长事务问题
在springboot项目中,我们开启事务是非常简单的,使用注解的方式就是在需要开启事务的方法上添加@Transactional,这样就可以实现这个方法里面的所有操作和调用方法的操作都绑定在一个事务上面,要么全部一起执行成功,要么全部一起执行失败,如果其中有某个地方抛了异常,则整个方法涉及的事务操作都会回滚,但是如果随意滥用@Transactional,又有可能引发长事务问题,导致数据库死锁、数据库连接池占满等问题。
-
css实现“展开阅读全文”功能
最近发现很多博客网站,资讯网站喜欢把资讯博文,内容等这些大文本的信息在页面显示的时候都会有个“展开阅读全文”的按钮,点击这个按钮即可展开显示所有的内容,不然一开始就显示那么长的篇幅相对来说既不美观,又对用户体验不好。现在就让我来仿照这类网站实现一个“展开阅读全文”功能。这里主要用到的前端技术是html+jquery+css,只做展开功能,没做收起功能(收起功能没必要吧,谁会去收起呀???)。
-
关于编程中面向对象的理解,什么是面向对象
面向对象设计相对于结构化程序设计可以说是一种更优秀的程序设计方法。它的基本思想是使用类、对象、继承、封装、消息等基本概念进行程序设计。它是从现实世界中客观存在的事物(即对象)出发来构造软件系统,并在软件系统构造中尽可能运用人类的自然思维方式,强调直接以现实世界中的事物(即对象)为中心来思考,认识问题。
-
我的linux操作命令总结,记录常用linux操作命令
平常本地开发项目使用的系统基本都是window系统,而且都是图形化操作,非常方便,window也是越做越好了,项目部署到生产环境一般都是选择linux系统(当然window server系列也可以),而linux一般则选择centOS居多,这里记录一下linux常用命令,以免老是过几天就忘了,后续不断补充。