玩转 SpringBoot 2.x 之使用 SpringDataJpa 篇
前言
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 篇相关推荐
- 玩转 SpringBoot 2 快速整合 | RESTful Api 篇
概述 RESTful 是一种架构风格,任何符合 RESTful 风格的架构,我们都可以称之为 RESTful 架构.我们常说的 RESTful Api 是符合 RESTful 原则和约束的 HTTP ...
- 玩转SpringBoot 2.x 解析BeanPostProcessor原理篇
专题系列分类:玩转SpringBoot2.x系列教程 1 demo 版本说明 开发工具:Spring Tool Suite (STS) springboot版本:2.0.5.RELEASE jdk版本 ...
- 玩转SpringBoot 2 快速搭建 | Spring Initializr 篇
SpringBoot 为我们提供了外网 Spring Initializr 网页版来帮助我们快速搭建 SpringBoot 项目,如果你不想用 IDEA 中的插件,这种方式也是不错的选择.闲话少说,直 ...
- 玩转 SpringBoot 2 快速搭建 | IntellJ IDEA篇
IntellJ IDEA 介绍 IntelliJ IDEA 简称 IDEA,目前被认为是最好用的开发Java 语言开发工具之一,不过是收费的.和其同类型的工具有 Eclipse 和 MyEclipse ...
- 玩转 SpringBoot 2 之整合 JWT 下篇
前言 在<玩转 SpringBoot 2 之整合 JWT 上篇> 中介绍了关于 JWT 相关概念和JWT 基本使用的操作方式.本文为 SpringBoot 整合 JWT 的下篇,通过解决 ...
- onresize事件会被多次触发_玩转SpringBoot之通过事件机制参与SpringBoot应用的启动过程...
生命周期和事件监听 一个应用的启动过程和关闭过程是归属到"生命周期"这个概念的范畴. 典型的设计是在启动和关闭过程中会触发一系列的"事件",我们只要监听这些事件 ...
- 玩转springboot:默认静态资源和自定义静态资源实战
点个赞,看一看,好习惯!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾 ...
- @retention注解作用_分分钟带你玩转SpringBoot自定义注解
在工作中,我们有时候需要将一些公共的功能封装,比如操作日志的存储,防重复提交等等.这些功能有些接口会用到,为了便于其他接口和方法的使用,做成自定义注解,侵入性更低一点.别人用的话直接注解就好.下面就来 ...
- 玩转 SpringBoot 2 之整合 JWT 上篇
前言 该文主要带你了解什么是 JWT,以及JWT 定义和先关概念的介绍,并通过简单Demo 带你了解如何使用 SpringBoot 2 整合 JWT. 介绍前在这里我们来探讨一下如何学习一门新的技术, ...
最新文章
- 像不像等待项目上线的你
- CentOS7.0 OpenWrt环境搭建
- myid文件到底是否需要自己手动配置
- python字符串三种常用的方法或函数_python中字符串常用的函数
- efm32芯片电压_解读GP21+EFM32低功耗热量表电路
- Javascript插入排序
- 阿里云POLARDB 2.0重磅来袭!为何用户如此的期待?
- js 处理 cookie的存储与删除
- 以树之名,诉春之情--F1503005班团改金
- 服务器有什么类型?应用功能是什么
- composer下面composer.lock有什么用处 以及 如何优雅地删除它
- 在计算机中无符号整数和有符号整数,无符号整数和有符号整数怎么区分?
- Ethercat远程IO模块接线准则
- 春节流量争夺战:互联网巨头跪求你收红包
- 科学计算机怎么算锥度,如何计算锥度值?比如说1:20等于多少度?请说说具体步骤!-锥度的计算公式-数学-贾儆刹同学...
- SQL语句习题总练习
- SUST OJ 1671: 数字拼图
- 江小白:情怀之下,举步维艰
- matlab for 语句事例,有关matlab里面for语句的使用。
- 传国宝玺 第四部 港岛邪云 第五十八章 启尸招魂
热门文章
- properties 配置回车_Dell?H730P?Raid1?配置方法
- 在自己的电脑上搭建服务器(可供对外访问)
- c语言学习-有一12个元素的整型数组b,从键盘输入数据,请以每行4个数据各个数据之间空两格的形式输出b数组的12个元素
- 项目管理工具project软件学习(一) - 项目信息设置
- 使用实时摄像头预览的iOS对象检测(六)
- 使用VSTS为ASP.NET Core构建DevOps CI/CD管道
- ASP.NET Core 2.0身份和角色管理入门
- native下拉图片放大 react_RN下拉图片放大 - Chason-洪的个人空间 - OSCHINA - 中文开源技术交流社区...
- python从mongodb里取出数据进行可视化_Python数据分析及可视化实例之MongoDB增删改查...
- linux 7 postconf,postconf 命令常用参数