文章目录
1. 整体认识JPA
为什么要重新学习“Spring Data JPA”?俗话说的好:“未来已经来临,只是尚未流行”,纵观市场上的 ORM 框架,MyBatis 以灵活著称,但是要维护复杂的配置,并且不是 Spring 官方的天然全家桶,还得做额外的配置工作,如果资深的架构师还得做很多封装;Hibernate 以 HQL 和关系映射著称,但是就是使用起来不是特别灵活;那么 Spring Data JPA 来了,感觉要夺取 ORM 的 JPA 霸主地位了,底层以 Hibernate 为封装,对外提供了超级灵活的使用接口,又非常符合面向对象和 Rest 的风格,感觉是架构师和开发者的福音,并且 Spring Data JPA 与 Spring Boot 配合起来使用具有天然的优势,你会发现越来越多的公司的招聘要用会有传统的 SSH、Spring、MyBatis 要求,逐步的变为 Spring Boot、Spring Cloud、Spring Data 等 Spring 全家桶的要求,而很多新生代的架构师基于其生态的考虑,正在逐步推动者 Spring Data JPA 的更多的使用场景。
2. 市场上ORM框架对比
- MyBatis:MyBatis 本是 Apache 的一个开源项目 iBatis,2010 年这个项目由 Apache Software Foundation 迁移到了 Google Code,并且改名为 MyBatis,其着力于 POJO 与 SQL 之间的映射关系,可以进行更为细致的 SQL,使用起来十分灵活、上手简单、容易掌握,所以深受开发者的喜欢,目前市场占有率最高,比较适合互联应用公司的 API 场景;缺点就是工作量比较大,需要各种配置文件的配置和 SQL 语句。
- Hibernate:Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库,并且对象有自己的生命周期,着力点对象与对象之间关系,有自己的 HQL 查询语言,所以数据库移植性很好。Hibernate 是完备的 ORM 框架,是符合 JPA 规范的,有自己的缓存机制,上手来说比较难,比较适合企业级的应用系统开发。
- Spring Data JPA:可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现,引用 JPQL(Java Persistence Query Language)查询语言,属于 Spring 的整个生态体系的一部分。由于 Spring Boot 和 Spring Cloud 在市场上的流行,Spring Data JPA 也逐渐进入大家的视野,他们有机的整体,使用起来比较方便,加快了开发的效率,使开发者不需要关系和配置更多的东西,完全可以沉浸在 Spring 的完整生态标准的实现下,上手简单、开发效率高,又对对象的支持比较好,又有很大的灵活性,市场的认可度越来越高。
- OpenJPA :是 Apache 组织提供的开源项目,它实现了 EJB 3.0 中的 JPA 标准,为开发者提供功能强大、使用简单的持久化数据管理框架,但功能、性能、普及性等方面更加需要加大力度,所以用的人不人不是特别多。
- QueryDSL:QueryDSL 可以在任何支持的 ORM 框架或者 SQL 平台上以一种通用的 API 方式来构建查询,目前 QueryDSL 支持的平台包括 JPA、JDO、SQL、Java Collections、RDF、Lucene、Hibernate Search,同时 Spring Data JPA 也对 QueryDSL 做了很好的支持。
3. SpringBoot Jpa
Spring Boot Jpa 是 Spring 基于 ORM 框架、Jpa 规范的基础上封装的一套 Jpa 应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data Jpa 可以极大提高开发效率!
3.1 基础查询
基本查询也分为两种,一种是 Spring Data 默认已经实现,一种是根据查询的方法来自动解析成 SQL。
预先生成方法
Spring Boot Jpa 默认预先生成了一些基本的CURD的方法,例如:增、删、改等等
** 引入依赖**
<!--1 确定spring boot的版本-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<!--test测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<!--web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--通用mapper起步依赖-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
<!--MySQL数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
配置yml文件
#端口号
server:
port: 9090
#服务名和数据源(连接池)
spring:
application:
name: activitiservice
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/activiti6_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&nullCatalogMeansCurrent=true
username: root
password: 1234
jpa:
show-sql: true
编写实体类
package com.yb.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* @author liangjie@itcast.cn
* @version 1.0
* @date 2020/7/31
*/
@Data
@Entity
@Table(name = "act_hi_taskinst")
public class HiTaskinst {
@Id
@Column(name = "ID_")
private String id;
@Column(name = "PROC_DEF_ID_")
private String procDefId;
@Column(name = "TASK_DEF_KEY_")
private String taskDefKey;
@Column(name = "PROC_INST_ID_")
private String procInstId;
@Column(name = "EXECUTION_ID_")
private String executionId;
@Column(name = "NAME_")
private String name;
@Column(name = "PARENT_TASK_ID_")
private String parentTaskId;
@Column(name = "DESCRIPTION_")
private String description;
@Column(name = "OWNER_")
private String owner;
@Column(name = "ASSIGNEE_")
private String assignee;
@Column(name = "START_TIME_")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date startTime;
@Column(name = "CLAIM_TIME_")
private Date claimTime;
@Column(name = "END_TIME_")
private Date endTime;
@Column(name = "DURATION_")
private Long duration;
@Column(name = "DELETE_REASON_")
private String deleteReason;
@Column(name = "PRIORITY_")
private Integer priority;
@Column(name = "DUE_DATE_")
private Date dueDate;
@Column(name = "FORM_KEY_")
private String formKey;
@Column(name = "CATEGORY_")
private String category;
@Column(name = "TENANT_ID_")
private String tenantId;
}
编写HiTaskinstRepository接口,只需继承JpaRepository即可
public interface HiTaskinstRepository extends JpaRepository<HiTaskinst,String> {
}
编写JpaService接口
public interface JpaService {
Optional<HiTaskinst> test01(String s);
}
JpaServiceImpl类
@Service
public class JpaServiceImpl implements JpaService {
@Resource
private HiTaskinstRepository hiTaskinstRepository;
@Override
public Optional<HiTaskinst> test01(String s) {
return hiTaskinstRepository.findById(s);
}
}
编写JpaController
@RestController
@RequestMapping("/jpa")
public class JpaController {
@Resource
private JpaService jpaService;
@GetMapping
private HiTaskinst test01(){
Optional<HiTaskinst> byId = jpaService.test01("2505");
if(byId.isPresent()){
HiTaskinst hiTaskinst = byId.get();
System.out.println(hiTaskinst);
return hiTaskinst;
}
return null;
}
}
测试
浏览器直接访问看运行结果
3.2 使用自己编写方法实现CRUD
3.2.1 自定义查询
JPA中一些常用的查询操作
//And --- 等价于 SQL 中的 and 关键字,比如 findByHeightAndSex(int height,char sex);
public List<HiTaskinst> findByHeightAndSex(int height,char sex);
// Or --- 等价于 SQL 中的 or 关键字,比如 findByHeightOrSex(int height,char sex);
public List<HiTaskinst> findByHeightOrSex(int height,char sex);
//Between --- 等价于 SQL 中的 between 关键字,比如 findByHeightBetween(int min, int max);
public List<HiTaskinst> findByHeightBetween(int min,int max);
//LessThan --- 等价于 SQL 中的 "<",比如 findByHeightLessThan(int max);
public List<HiTaskinst> findByHeightLessThan(int max);
//GreaterThan --- 等价于 SQL 中的">",比如 findByHeightGreaterThan(int min);
public List<HiTaskinst> findByHeightGreaterThan(int min);
//IsNull --- 等价于 SQL 中的 "is null",比如 findByNameIsNull();
public List<HiTaskinst> findByNameIsNull();
//IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByNameIsNotNull();
public List<HiTaskinst> findByNameIsNotNull();
//NotNull --- 与 IsNotNull 等价;
public List<HiTaskinst> findByNameNotNull();
//Like --- 等价于 SQL 中的 "like",比如 findByNameLike(String name);
public List<HiTaskinst> findByNameLike(String name);
//NotLike --- 等价于 SQL 中的 "not like",比如 findByNameNotLike(String name);
public List<HiTaskinst> findByNameNotLike(String name);
//OrderBy --- 等价于 SQL 中的 "order by",比如 findByNameNotNullOrderByHeightAsc();
public List<HiTaskinst>findByNameNotNullOrderByHeightAsc();
//Not --- 等价于 SQL 中的 "! =",比如 findByNameNot(String name);
public List<HiTaskinst> findByNameNot(String name);
//In --- 等价于 SQL 中的 "in",比如 findByNameIN(String name);
public List<HiTaskinst> findByNameIn(String name);
//NotIn --- 等价于 SQL 中的 "not in",比如 findByNameNotIN(String name);
public List<HiTaskinst> findByNameNotIn(String name);
在HiTaskinstRepository中添加方法
public interface HiTaskinstRepository extends JpaRepository<HiTaskinst,String> {
/**
* 通过名称查询
* @param name
* @return
*/
List<HiTaskinst> findByNameLike(String name);
}
在controller中添加代码测试
/**
* 通过名称查询-自定义方法
* @param name
* @return
*/
@GetMapping("/test02/{name}")
private List<HiTaskinst> test02(@PathVariable("name") String name) {
System.out.println(name);
List<HiTaskinst> list = jpaService.test02(name);
System.out.println(list.size());
for (HiTaskinst hiTaskinst : list) {
System.out.println(hiTaskinst);
}
return list;
}
JpaServiceImpl中方法
/**
* 通过名称查询-自定义方法
* @param name
* @return
*/
@Override
public List<HiTaskinst> test02(String name) {
//自定义方法
List<HiTaskinst> byNameLike = hiTaskinstRepository.findByNameLike(name);
return byNameLike;
}
测试
浏览器直接访问看运行结果
这是因为 jpa 通过关键字以及驼峰命名解析,findByName 会帮你解析成select from user where name like ?从而实现查询。
3.2.2 复杂的关联查询
一些比较复杂的关联查询要怎么实现呢,JPA的处理方法是:利用原生的SQl命令来实现那些复杂的关联查询
JpaController
package com.yb.controller;
import com.yb.domain.HiTaskinst;
import com.yb.service.JpaService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
/**
* @author liangjie@itcast.cn
* @version 1.0
* @date 2020/7/31
*/
@RestController
@RequestMapping("/jpa")
public class JpaController {
@Resource
private JpaService jpaService;
/**
* 测试基础查询-通过id查询
* @return
*/
@GetMapping
private HiTaskinst test01(){
Optional<HiTaskinst> byId = jpaService.test01("2505");
if(byId.isPresent()){
HiTaskinst hiTaskinst = byId.get();
System.out.println(hiTaskinst);
return hiTaskinst;
}
return null;
}
/**
* 通过名称查询-自定义方法
* @param name
* @return
*/
@GetMapping("/test02/{name}")
private List<HiTaskinst> test02(@PathVariable("name") String name) {
System.out.println(name);
List<HiTaskinst> list = jpaService.test02(name);
System.out.println(list.size());
for (HiTaskinst hiTaskinst : list) {
System.out.println(hiTaskinst);
}
return list;
}
/**
* 通过名称查询-原生SQL进行查询操作
* @param name
* @return
*/
@GetMapping("/test03/{name}")
private List<HiTaskinst> test03(@PathVariable("name") String name) {
System.out.println(name);
List<HiTaskinst> list = jpaService.test03(name);
System.out.println(list.size());
for (HiTaskinst hiTaskinst : list) {
System.out.println(hiTaskinst);
}
return list;
}
}
HiTaskinstRepository
package com.yb.dao;
import com.yb.domain.HiTaskinst;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
* @author liangjie@itcast.cn
* @version 1.0
* @date 2020/7/31
*/
public interface HiTaskinstRepository extends JpaRepository<HiTaskinst,String> {
/**
* 通过名称查询
* @param name
* @return
*/
List<HiTaskinst> findByNameLike(String name);
/**
* 原生SQL
* @param name
* @return
*/
@Query(value = "select ht.* from `act_hi_taskinst` ht WHERE ht.`NAME_` = ?1",
nativeQuery = true)
@Modifying
List<HiTaskinst> findHiTaskinstByName(String name);
}
测试
3.2.3 JPA分页查询
3.2.3.1 普通分页查询
JpaController
/**
* 分页查询
* @param pageNum
* @param pageSize
* @return
*/
@GetMapping("/test04")
private List<HiTaskinst> test04(Integer pageNum,Integer pageSize) {
System.out.println(pageNum+"==="+pageSize);
PageRequest pageRequest = new PageRequest(pageNum-1, pageSize);
Page<HiTaskinst> pages = jpaService.test04(pageRequest);
List<HiTaskinst> content = pages.getContent();
System.out.println(content.size());
return content;
}
JpaServiceImpl
/**
* 分页查询
* @param pageRequest
* @return
/
@verride
public Page<HiTaskinst> test04(PageRequest pageRequest) {
Page<HiTaskinst> byNameNot = hiTaskinstRepository.findAll(pageRequest);
return byNameNot;
}
HiTaskinstRepository
/**
* 分页查询
* @param pageable
* @return
*/
Page<HiTaskinst> findAll(Pageable pageable);
测试
浏览器直接访问看运行结果,查询第一页,每页两条数据。
3.2.3.2 排序分页查询
JpaController
/**
* 分页查询+排序
* @param pageNum
* @param pageSize
* @return
*/
@GetMapping("/test05")
private List<HiTaskinst> test05(Integer pageNum,Integer pageSize) {
System.out.println("排序分页:"+pageNum+"==="+pageSize);
Page<HiTaskinst> pages = jpaService.test05(pageNum,pageSize);
List<HiTaskinst> content = pages.getContent();
System.out.println(content.size());
return content;
}
JpaServiceImpl
/**
* 排序分页查询
* @param pageNum
* @param pageSize
* @return
*/
@Override
public Page<HiTaskinst> test05(Integer pageNum, Integer pageSize) {
//设置以表中具体字段来进行排序,desc倒序,asc正序
Sort sort = new Sort(Sort.Direction.DESC,"endTime");
PageRequest pageRequest = new PageRequest(pageNum - 1, pageSize, sort);
Page<HiTaskinst> all = hiTaskinstRepository.findAll(pageRequest);
return all;
}
HiTaskinstRepository
/**
* 分页查询
* @param pageable
* @return
*/
Page<HiTaskinst> findAll(Pageable pageable);
测试
浏览器直接访问看运行结果,查询第一页,每页两条数据。此查询以endTime结束时间来排序,倒序
3.2.3.3 条件分页查询
JpaController
/**
* 分页查询+条件
* @param pageNum
* @param pageSize
* @return
*/
@GetMapping("/test06/{name}")
private List<HiTaskinst> test06(@PathVariable("name") String name,Integer pageNum,Integer pageSize) {
System.out.println("条件分页:"+pageNum+"==="+pageSize);
Page<HiTaskinst> pages = jpaService.test06(name,pageNum,pageSize);
List<HiTaskinst> content = pages.getContent();
System.out.println(content.size());
return content;
}
JpaServiceImpl
/**
* 分页查询+条件
* @param name
* @param pageNum
* @param pageSize
* @return
*/
@Override
public Page<HiTaskinst> test06(String name, Integer pageNum, Integer pageSize) {
PageRequest pageRequest = new PageRequest(pageNum - 1, pageSize);
return hiTaskinstRepository.findByNameLike(name,pageRequest);
}
HiTaskinstRepository
/**
* 分页查询+条件(根据名称查询)
* @param name
* @param pageable
* @return
*/
Page<HiTaskinst> findByNameLike(String name,Pageable pageable);
测试
浏览器直接访问看运行结果,查询第一页,每页两条数据,这里传输的条件是name=班长审批,数据库就这一条,所以效果只有一条信息
3.2.4 JPA添加
JpaController
/**
* JPA添加
* @return
*/
@PostMapping("/add")
public HiTaskinst addHiTaskinst(@RequestBody HiTaskinst hiTaskinst){
System.out.println(hiTaskinst);
HiTaskinst h = jpaService.addHiTaskinst(hiTaskinst);
return h;
}
JpaServiceImpl
/**
* 添加-save
* @param hiTaskinst
* @return
*/
@Override
public HiTaskinst addHiTaskinst(HiTaskinst hiTaskinst) {
HiTaskinst save = hiTaskinstRepository.save(hiTaskinst);
return save;
}
测试
3.2.4 JPA删除
JpaController
/**
* JPA删除
* @return
*/
@DeleteMapping("/delete/{id}")
public String deleteHiTaskinst(@PathVariable("id") String id){
System.out.println(id);
String h = jpaService.deleteHiTaskinst(id);
return h;
}
JpaServiceImpl
/**
* JPA删除
* @param id
* @return
*/
@Override
public String deleteHiTaskinst(String id) {
try {
hiTaskinstRepository.deleteById(id);
} catch (Exception e) {
e.printStackTrace();
return "服务器繁忙";
}
return "删除成功";
}
3.2.5 JPA修改
JpaController
/**
* JPA修改
* @return
*/
@PutMapping("/put")
public HiTaskinst putHiTaskinst(@RequestBody HiTaskinst hiTaskinst){
System.out.println(hiTaskinst);
HiTaskinst h = jpaService.putHiTaskinst(hiTaskinst);
return h;
}
JpaServiceImpl
/**
* 修改
* @param hiTaskinst
* @return
*/
@Override
public HiTaskinst putHiTaskinst(HiTaskinst hiTaskinst) {
HiTaskinst save = hiTaskinstRepository.save(hiTaskinst);
return save;
}
测试