前言

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。

–以上内容摘抄自百度百科

JPA Hibernate SpringDatAJpa 三者的关系

JPA 是一种规范而 Hibernate 和 SpringDataJpa 是 JPA 的具体实现, 另外Spring Data JPA 是 Spring 在 Hibernate 的基础上构建的 JPA 使用解决方案。值得一提的是在早期的时候 Hibernate是不支持JPA,因为Sun在提出 JPA 的时候就已经有 Hibernate 了。

SpirngBoot JPA 操作实战

内置查询接口

第一步:引入 SpirngBoot JPA 的 starter 依赖,同时需要引入mysql 驱动依赖。具体代码如下:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>

第二步:添加Mysql 数据源的配置

在 SpringBoot 的配置文件 application.yml 中添加数据库配置信息。

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/learn?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=truedriver-class-name: com.mysql.jdbc.Driverusername: rootpassword: root# Ddl-auto :  Create: 自动创建表 Update:自动修改表  Create-drop:应用停下来会把表删除掉 None:什么都不做  Validate:类和表明是否一致jpa:show-sql: truehibernate:ddl-auto: updateproperties:hibernate:format_sql: truedialect: org.hibernate.dialect.MySQL5InnoDBDialect

在启动的时候报如下错误

java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.

at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.13.jar:8.0.13]at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.13.jar:8.0.13]at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89) ~[mysql-connector-java-8.0.13.jar:8.0.13]at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63) ~[mysql-connector-java-8.0.13.jar:8.0.13]

原来的url内容是: url: jdbc:mysql://127.0.0.1:3306/learn

将url 内容修改为

url: jdbc:mysql://127.0.0.1:3306/learn?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true 问题解决。

第三步:根据 JPA 规范的注解配置映射实体

添加映射实体通过JPA规范的注解,具体代码如下:

package cn.lijunkui.springbootlearn.test.model;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;@Entitypublic class User {@Id@GeneratedValueprivate Long id;private String name;private Integer age;public Long getId() {return id;}public void setId(Long id) {this.id = id;}private String sex;private String address;public  User(){}public User(Long id,String name,Integer age,String sex,String address){this.id = id;this.name = name;this.age = age;this.address = address;this.sex = sex;}// 此处省略get 和set 方法}

第四步:使用Spring Data Jpa 内置查询接口 CrudRepository 充当DAO,具体操作方式是定义接口然后继承CrudRepository即可。具体代码如下:

package cn.lijunkui.springbootlearn.test.dao;import cn.lijunkui.springbootlearn.test.model.User;import org.springframework.data.repository.CrudRepository;import org.springframework.transaction.annotation.Transactional;import java.util.List;public interface UserCrudRepository extends CrudRepository<User,Long>{}

你肯定会惊讶 这就完成啦,我可以确定的告诉我们的Dao开发完毕 基本的增删改查搞定。

查看CrudRepository源码我们发现他有的方法如下:

<S extends T> S save(S entity);//新增<S extends T> Iterable<S> saveAll(Iterable<S> entities);//批量新增Optional<T> findById(ID id);//查询通过idboolean existsById(ID id);//id是否存在Iterable<T> findAll();//查询所有Iterable<T> findAllById(Iterable<ID> ids);//查询多个id的数据long count();//数据的总数void deleteById(ID id);//根据id进行删除void delete(T entity);//根据实例进行删除void deleteAll(Iterable<? extends T> entities);//批量删除void deleteAll();//删除所有

第五步:编写测试用例。

SpringBoot 的单元测试 需要我们声明 @SpringBootTest 和 @RunWith 注解

这里只是简单写啦几个测试。

package cn.lijunkui.springbootlearn.test.dao;@SpringBootTest@RunWith(SpringRunner.class)public class UserCrudRepositoryTest {@Autowiredprivate UserCrudRepository userCrudRepository;/***  添加用户 测试*/@Testpublic void add(){User user = new User();user.setName("ljk2");user.setSex("1");user.setAge(18);user.setAddress("beijing");User result = userCrudRepository.save(user);Assert.assertNotNull(result);}/***  修改用户*/@Testpublic void edit(){User user = new User();user.setId(1l);user.setName("ljk2edit");user.setSex("1");user.setAge(18);user.setAddress("beijing");User result = userCrudRepository.save(user);Assert.assertNotNull(result);}/*** 通过id 进行查找*/@Testpublic void findById(){Optional<User> userOptional = userCrudRepository.findById(1l);User result = userOptional.get();Assert.assertNotNull(result);}/***  查询所有*/@Testpublic void findAll(){List<User> userList = (List<User>)userCrudRepository.findAll();Assert.assertTrue(userList.size()>0);}@Testpublic void count(){long count = userCrudRepository.count();System.out.println(count);}}

到这里最简单SpringDataJAP 介绍完毕。接下来让我们继续深入SpringDataJAP其他内置接口

PagingAndSortRepository 使用介绍

CrudRepository 只是具有增删改查的一些基本功能,接下来 PagingAndSortingRepository是具有分页和排序的功能 同时他继承啦 CrudRepository。

编写测试用例:


package cn.lijunkui.springbootlearn.test.dao;import cn.lijunkui.springbootlearn.test.model.User;import org.hibernate.criterion.Order;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import org.springframework.data.domain.Sort;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;import static org.junit.Assert.*;@SpringBootTest@RunWith(SpringRunner.class)public class UserPageRepositoryTest {@Autowiredprivate UserPageRepository userPageRepository;@Testpublic void findAllBySort(){List<User> userList = (List<User>)userPageRepository.findAll(new Sort(Sort.Direction.ASC,"age"));System.out.println(userList.size());}@Testpublic void findAllByPageable(){Page<User> userPage =  userPageRepository.findAll(new PageRequest(0, 20));userPage.getNumber();//页数userPage.getContent();//分页的数据userPage.getTotalPages();//总共的页数System.out.println("number:"+userPage.getNumber()+"Countet"+userPage.getContent().size()+"TotalPages"+userPage.getTotalPages());}}

JpaRepository 使用介绍

JpaRepository 不仅继承啦 PagingAndSortingRepository 同时继承啦 QueryByExampleExecutor(示例匹配器)

通过我们的测试用例查询期详细的用法


package cn.lijunkui.springbootlearn.test.dao;import cn.lijunkui.springbootlearn.test.model.ResultDTO;import cn.lijunkui.springbootlearn.test.model.User;import org.junit.Assert;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.domain.*;import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;import java.util.List;import java.util.Map;import static org.junit.Assert.*;@SpringBootTest@RunWith(SpringRunner.class)public class UserJpaRepositoryTest {@Autowiredprivate UserJpaRepository userJpaRepository;/*** 执行秒数:49422 49145* 批量保存数据*/@Testpublic void BatchSave(){long startTime = System.currentTimeMillis();List<User> list = new ArrayList<User>();for (int i = 0; i < 60000; i++) {User user = new User();user.setName("ljk"+i);user.setAge(i);user.setAddress("address"+i);list.add(user);if(i%100 == 0){userJpaRepository.saveAll(list);list.clear();}}long endTime = System.currentTimeMillis();System.out.println("执行秒数:"+ (endTime - startTime));}/*** 执行秒数:48053 48394 执行速度比BatchSave 要快* 批量保存数据 (高效处理方式)减少大事物的提交*/@Testpublic void BatchSaveBest(){long startTime = System.currentTimeMillis();List<User> list = new ArrayList<User>();for (int i = 0; i < 60000; i++) {User user = new User();user.setName("ljk"+i);list.add(user);if(i%100 == 0){userJpaRepository.saveAll(list);userJpaRepository.flush();list.clear();}}long endTime = System.currentTimeMillis();System.out.println("执行秒数:"+ (endTime - startTime));}/*** 查询所有数据*/@Testpublic void findALL(){List<User> userlists = userJpaRepository.findAll();Assert.assertTrue(userlists.size() > 0);}/*** 根据 age 排序查询*/@Testpublic void findALLSortAge(){List<User> lists = userJpaRepository.findAll(Sort.by(Sort.Direction.ASC ,"age"));for (User list : lists) {System.out.println(list);}}/*** 分页查询*/@Testpublic void  findAllByPage(){PageRequest pageRequest = new PageRequest(0,1);Page<User> userPage = userJpaRepository.findAll(pageRequest);Assert.assertTrue(userPage.getContent().size() == 1);}/***  分页排序查询*/@Testpublic void findAllByPageAndSort(){PageRequest pageRequest = new PageRequest(0,3,Sort.by(Sort.Direction.ASC ,"age"));Page<User> userPage =  userJpaRepository.findAll(pageRequest);List<User> userList= userPage.getContent();for (User user : userList) {System.out.println(user);}}/*** 根据id 的集合获取所有数据*/@Testpublic void findAllByIds(){List<Long> ids = new ArrayList<Long>();ids.add(1l);ids.add(2l);ids.add(3l);ids.add(4l);List<User> userList = userJpaRepository.findAllById(ids);Assert.assertTrue(userList.size()>0);}/*** 批量删除所有数据*/@Testpublic void  deleteAllInBatch(){userJpaRepository.deleteAllInBatch();}/***  保存数据并刷新缓存*/@Testpublic void saveAndFlush(){User user = new User();user.setName("ljk");user.setAge(18);user.setAddress("beijing");user.setSex("1");User result = userJpaRepository.saveAndFlush(user);Assert.assertNotNull(result);}/*** 批量删除*/@Testpublic void deleteInBatch(){List<User> userList = new ArrayList<User>();User user = new User();user.setId(1l);userList.add(user);User user2 = new User();user2.setId(2l);userList.add(user2);User user3 = new User();user3.setId(3l);userList.add(user3);User user4 = new User();user4.setId(4l);userList.add(user4);userJpaRepository.deleteInBatch(userList);}/*** 根据id 获取数据 延迟加载*/@Testpublic void getOne(){User result = userJpaRepository.getOne(1l);Long id = result.getId();String name = result.getName();System.out.println(id);System.out.println(name);Assert.assertNotNull(result);}/*** 示例匹配器 ExampleMatcher*/@Testpublic void findUserByExam(){User user = new User();user.setName("ljk");List<User> list = userJpaRepository.findAll(Example.of(user));System.out.println(list.size());}@Testpublic void findUserByExamQuery(){User user = new User();user.setName("ljk");user.setAddress("address8");user.setAge(8);ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("name", ExampleMatcher.GenericPropertyMatchers.startsWith())//模糊查询匹配开头,即{username}%.withMatcher("address" ,ExampleMatcher.GenericPropertyMatchers.contains())//全部模糊查询,即%{address}%.withIgnorePaths("id");//忽略字段,即不管id是什么值都不加入查询条件Example<User> example = Example.of(user ,matcher);List<User> userList = userJpaRepository.findAll(example);Assert.assertTrue(userList.size() > 0);}}

方法名称创建查询

虽然 SpringDataJPA 提供内置查询接口,自定义查询方式它提供了方法名称的方式进行查询,只需要定义个接口方法你就可以进行查询你想要的数据。


@SpringBootTest@RunWith(SpringRunner.class)public class UserJpaRepositoryTest {@Autowiredprivate UserJpaRepository userJpaRepository;@Testpublic void findByNameAndAge(){List<User> userList = userJpaRepository.findByNameAndAge("ljk",18);Assert.assertTrue( userList.size()>0 );}public void findByNameOrAge(){List<User> userList = userJpaRepository.findByNameOrAge("ljk",18);Assert.assertTrue( userList.size()>0 );}}

快速自定以查询方法:示例如下

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1(parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1(parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1(parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection ages) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

计数查询

SpringDataJap 提供针对某个字段数量统计使用 countBy+数据库映射实体字段名称即可完成,代码如下所示:

 long countByName(String name);

计数删除

也可以通过 removeBy+数据库映射实体字段名称进行计数删除。

@TransactionalList<User> reomveByName(String name);

List 用于接收删除数据的信息

同时也可以通过 deleteBy+数据库映射实体字段名称进行数据的删除。

 @TransactionalList<User> deleteByName(String name);

@Query注解查询

Spring Data JPA 不仅提供内置接口和方法名称查询方式同时还提供了通过 @Query 注解的方式拼写查询语句,对于经常使用Hibernate HQL 查询的福音啊。

具体使用方式代码如下:

public interface UserJpaRepository extends JpaRepository<User,Long>{/*** 根据姓名查询用户* @param name* @return*/@Query("select u from User u where u.name = ?1")public List<User> findUserByNameByQuery(String name);/*** 根据姓名(like)和年龄查询用户* @param name* @param age* @return*/@Query("select u from User u where u.name like  CONCAT('%',?1,'%') and u.age = ?2" )public List<User> findUserByLikeNameByQuery(String name,Integer age);/*** 根据姓名(like)和年龄查询用户*  命名参数 进行查询*/@Query("select u from User u where u.name like CONCAT('%',:name,'%') and u.age = :age")public User findUserByNameAndAgeWithQery(@Param("name") String  name,@Param("age") Integer age);/*** 根据姓名(like)和年龄查询用户*  命名参数 原生方式进行查询*/@Query(value = "select * from user u where u.name like CONCAT('%',:name,'%') and u.age = :age",nativeQuery = true)public List<User> findUserByNameAndAgeWithQeryNative(@Param("name") String  name,@Param("age") Integer age);/*** 查询每个地区的人的个数* @return*/@Query("select new cn.lijunkui.springbootlearn.test.model.ResultDTO(u.address,count(u.id))  from User u group by u.address")public List<ResultDTO> findCountGroupByAddress();}

测试用例:

@SpringBootTest@RunWith(SpringRunner.class)public class UserJpaRepositoryTest {@Autowiredprivate UserJpaRepository userJpaRepository;@Testpublic void  findUserByNameByQuery(){List<User> userList = userJpaRepository.findUserByNameByQuery("ljk");Assert.assertNotNull(userList.size()>0);}@Testpublic void findUserByLikeNameByQuery(){List<User> userList = userJpaRepository.findUserByLikeNameByQuery("jk",18);Assert.assertNotNull(userList.size()>0);}@Testpublic void findUserByNameAndAgeWithQery(){User user = userJpaRepository.findUserByNameAndAgeWithQery("jk",18);Assert.assertNotNull(user);}@Testpublic void findUserByNameAndAgeWithQeryNative(){List<User> userList = userJpaRepository.findUserByNameAndAgeWithQeryNative("jk",18);Assert.assertNotNull(userList.size()>0);}/*** 零散参数的接收*/@Testpublic void  findCountGroupByAddress(){List<ResultDTO> results  = userJpaRepository.findCountGroupByAddress();System.out.println(results);}}

小结

SpringDataJpa 相对于Hibernate 的使用更为简洁,更容易快速上手SQL 逻辑不是很复杂的业务。如果你想更灵活的编写SQL 可以考虑使用Mybaties。 如果你还没有上手过 SpringDataJpa,还等什么抓紧跟着本文操作一遍吧。

代码示例

我本地环境如下:

  • SpringBoot Version: 2.1.0.RELEASE
  • Apache Maven Version: 3.6.0
  • Java Version: 1.8.0_144
  • IDEA:Spring Tools Suite (STS)

整合过程如出现问题可以在我的GitHub 仓库 springbootexamples 中模块名为 spring-boot-2.x-spring-data-jpa 项目中进行对比查看

GitHub:https://github.com/zhuoqianmingyue/springbootexamples

参考文献

https://docs.spring.io/spring-data/jpa/docs/2.0.10.RELEASE/reference/html/

玩转 SpringBoot 2.x 之使用 SpringDataJpa 篇相关推荐

  1. 玩转 SpringBoot 2 快速整合 | RESTful Api 篇

    概述 RESTful 是一种架构风格,任何符合 RESTful 风格的架构,我们都可以称之为 RESTful 架构.我们常说的 RESTful Api 是符合 RESTful 原则和约束的 HTTP ...

  2. 玩转SpringBoot 2.x 解析BeanPostProcessor原理篇

    专题系列分类:玩转SpringBoot2.x系列教程 1 demo 版本说明 开发工具:Spring Tool Suite (STS) springboot版本:2.0.5.RELEASE jdk版本 ...

  3. 玩转SpringBoot 2 快速搭建 | Spring Initializr 篇

    SpringBoot 为我们提供了外网 Spring Initializr 网页版来帮助我们快速搭建 SpringBoot 项目,如果你不想用 IDEA 中的插件,这种方式也是不错的选择.闲话少说,直 ...

  4. 玩转 SpringBoot 2 快速搭建 | IntellJ IDEA篇

    IntellJ IDEA 介绍 IntelliJ IDEA 简称 IDEA,目前被认为是最好用的开发Java 语言开发工具之一,不过是收费的.和其同类型的工具有 Eclipse 和 MyEclipse ...

  5. 玩转 SpringBoot 2 之整合 JWT 下篇

    前言 在<玩转 SpringBoot 2 之整合 JWT 上篇> 中介绍了关于 JWT 相关概念和JWT 基本使用的操作方式.本文为 SpringBoot 整合 JWT 的下篇,通过解决 ...

  6. onresize事件会被多次触发_玩转SpringBoot之通过事件机制参与SpringBoot应用的启动过程...

    生命周期和事件监听 一个应用的启动过程和关闭过程是归属到"生命周期"这个概念的范畴. 典型的设计是在启动和关闭过程中会触发一系列的"事件",我们只要监听这些事件 ...

  7. 玩转springboot:默认静态资源和自定义静态资源实战

    点个赞,看一看,好习惯!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾 ...

  8. @retention注解作用_分分钟带你玩转SpringBoot自定义注解

    在工作中,我们有时候需要将一些公共的功能封装,比如操作日志的存储,防重复提交等等.这些功能有些接口会用到,为了便于其他接口和方法的使用,做成自定义注解,侵入性更低一点.别人用的话直接注解就好.下面就来 ...

  9. 玩转 SpringBoot 2 之整合 JWT 上篇

    前言 该文主要带你了解什么是 JWT,以及JWT 定义和先关概念的介绍,并通过简单Demo 带你了解如何使用 SpringBoot 2 整合 JWT. 介绍前在这里我们来探讨一下如何学习一门新的技术, ...

最新文章

  1. 像不像等待项目上线的你
  2. CentOS7.0 OpenWrt环境搭建
  3. myid文件到底是否需要自己手动配置
  4. python字符串三种常用的方法或函数_python中字符串常用的函数
  5. efm32芯片电压_解读GP21+EFM32低功耗热量表电路
  6. Javascript插入排序
  7. 阿里云POLARDB 2.0重磅来袭!为何用户如此的期待?
  8. js 处理 cookie的存储与删除
  9. 以树之名,诉春之情--F1503005班团改金
  10. 服务器有什么类型?应用功能是什么
  11. composer下面composer.lock有什么用处 以及 如何优雅地删除它
  12. 在计算机中无符号整数和有符号整数,无符号整数和有符号整数怎么区分?
  13. Ethercat远程IO模块接线准则
  14. 春节流量争夺战:互联网巨头跪求你收红包
  15. 科学计算机怎么算锥度,如何计算锥度值?比如说1:20等于多少度?请说说具体步骤!-锥度的计算公式-数学-贾儆刹同学...
  16. SQL语句习题总练习
  17. SUST OJ 1671: 数字拼图
  18. 江小白:情怀之下,举步维艰
  19. matlab for 语句事例,有关matlab里面for语句的使用。
  20. 传国宝玺 第四部 港岛邪云 第五十八章 启尸招魂

热门文章

  1. properties 配置回车_Dell?H730P?Raid1?配置方法
  2. 在自己的电脑上搭建服务器(可供对外访问)
  3. c语言学习-有一12个元素的整型数组b,从键盘输入数据,请以每行4个数据各个数据之间空两格的形式输出b数组的12个元素
  4. 项目管理工具project软件学习(一) - 项目信息设置
  5. 使用实时摄像头预览的iOS对象检测(六)
  6. 使用VSTS为ASP.NET Core构建DevOps CI/CD管道
  7. ASP.NET Core 2.0身份和角色管理入门
  8. native下拉图片放大 react_RN下拉图片放大 - Chason-洪的个人空间 - OSCHINA - 中文开源技术交流社区...
  9. python从mongodb里取出数据进行可视化_Python数据分析及可视化实例之MongoDB增删改查...
  10. linux 7 postconf,postconf 命令常用参数