内容简介:Spring Boot JPA
JPA(Java Persistence API)是一套 Java 持久化规范 , 用于将应用程序中的对象映射到关系型数据库 。
应用程序的数据访问层通常为域对象提供创建 、 读取 、 更新和删除(CRUD)操作 , Spring Data JPA 提供了这方面的通用接口以及持久化存储特定的实现 , 它旨在简化数据访问层 。 作为应用程序的开发人员 , 你只需要编写数据库的存取接口 , 由 Spring 运行时自动生成这些接口的适当实现 , 开发人员不需要编写任何具体的实现代码 。
Spring Data JPA 选择目前最流行之一的 Hibernate 用作 JPA 实现的提供者 。
环境
- JDK 8
- Maven 3
- IntelliJ IDEA 2016
- Spring Boot 1.5.2.RELEASE
- MySQL 5.5
依赖配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <exclusions> <exclusion> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jdbc</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>2.6.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency>
应用配置
数据源配置
spring: datasource: url: jdbc:mysql://127.0.0.1/jpa_testing_repository username: root password: root driver-class-name: com.mysql.jdbc.Driver hikari: auto-commit: true connection-test-query: 'SELECT 1' maximum-pool-size: 10
自动更新表结构
spring: jpa: hibernate: ddl-auto: update
SQL 输出
logging: level: root: warn org.hibernate.SQL: debug
完整配置
src/main/resources/application.yml
spring: datasource: url: jdbc:mysql://127.0.0.1/jpa_testing_repository username: root password: root driver-class-name: com.mysql.jdbc.Driver hikari: auto-commit: true connection-test-query: 'SELECT 1' maximum-pool-size: 10 jpa: hibernate: ddl-auto: update logging: level: root: warn org.hibernate.SQL: debug
创建数据库脚本
CREATE DATABASE jpa_testing_repository DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
实体类定义
@Entity @Table public class Product{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String category; private Double price; private Integer stock; @Column(name = "CREATE_TIME") private Date createTime; // getters and setters @Override public String toString(){ return "{id=" + id + ", name=" + name + ", category=" + category + ", price=" + price + ", stock=" + stock + ", createTime=" + createTime + "}"; } }
核心接口
Spring Data JPA 核心接口列表:
接口 | 父接口 | 描述 |
---|---|---|
Repository | - | 标记接口 , Spring 组件扫描到能自动识别 |
CrudRepository | Repository | 提供一组 CRUD 操作方法 |
PagingAndSortingRepository | CrudRepository | 提供一组分页和 排序 的方法 |
JpaRepository | PagingAndSortingRepository | 额外提供一组实用的操作方法 |
CrudRepository
方法 | 描述 | SQL |
---|---|---|
S save(S) | 保存或更新实体对象 |
INSERT ... VALUES(?...)
或 UPDATE ... SET ...
|
Iterable<S> save(Iterable<S>) | 保存集合里面所有的实体对象 |
产生多条: INSERT ... VALUES(?...)
|
T findOne(ID) | 根据主键查找 , 若查找不到则返回 null |
SELECT ... WHERE ID = ?
|
boolean exists(ID) | 判断给定的主键的记录是否存在 |
SELECT COUNT(*) ... WHERE ID = ?
|
Iterable<T> findAll() | 查找所有的记录 |
SELECT ... FROM ...
|
Iterable<T> findAll(Iterable<ID>) | 查找给定的主键集合的所有记录 |
SELECT ... WHERE ID IN(...)
|
long count() | 统计数据库中记录的总条数 |
SELECT COUNT(*) ...
|
void delete(ID) | 根据主键删除记录 , 如果主键不存在则抛出异常 |
1. SELECT ... WHERE ID = ?
2. DELETE ... WHERE ID = ?
|
void delete(T) |
删除参数给定的实体对象记录 ,
注意 ,
它只跟实体对象参数的 ID 属性值有关: 1.如果实体对象参数的 ID 属性没有设值或该值在数据库表中找不到对应的记录 , 则会先产生 INSERT
SQL ,
然后再删除刚刚插入的记录 ,
实际上做的是无用功; 2.如果实体对象参数的 ID 属性有值且在数据库表中有对应的记录 , 则将其删除 |
1. SELECT ... WHERE ID = ?
2. DELETE ... WHERE ID = ?
或: 1. INSERT ... VALUES(?...)
2. DELETE ... WHERE ID = ?
|
void delete(Iterable<? extends T>) | 删除集合里面所有的实体对象记录 |
产生多条: 1. SELECT ... WHERE ID = ?
2. DELETE ... WHERE ID = ?
或: 1. INSERT ... VALUES(?...)
2. DELETE ... WHERE ID = ?
|
void deleteAll() | 删除数据库中所有的记录 |
产生一条: SELECT ... FROM ...
产生多条: DELETE ... WHERE ID = ?
|
PagingAndSortingRepository
方法 | 描述 | SQL |
---|---|---|
Iterable<T> findAll(Sort sort) | 排序查询 |
SELECT ... ORDER BY ...
|
Page<T> findAll(Pageable pageable) | 分页查询 |
1. SELECT ... LIMIT ...
2. SELECT COUNT(ID) ...
|
Page
方法 | 描述 |
---|---|
totalElements | 数据库中总的记录条数 |
totalPages | 分页的总页数 |
size | 当前分页的数据含有的记录条数 |
content | 数据内容集合 |
sort | 分页查询的排序对象 |
isFirst() | 是否是第一页 |
isLast() | 是否是最后一页 |
hasContent() | 当前的分页数据是否内容 |
hasNext() | 是否有下一页 |
hasPrevious() | 是否有上一页 |
nextPageable() | 下一页的分页对象参数 |
previousPageable() | 上一页的分页对象参数 |
排序查询示例
@Override public void run(String... args) throws Exception{ List<Order> orders = new ArrayList<>(); orders.add(new Order("sex")); orders.add(new Order(Direction.DESC, "createTime")); orders.add(new Order("email").with(Direction.DESC)); userRepository.findAll(new Sort(orders)).forEach(System.out::println); }
分页查询示例
@Override public void run(String... args) throws Exception{ // 分页索引从0开始, 表示第一页 Page<User> page = userRepository.findAll(new PageRequest(0, 2)); page.forEach(System.out::println); }
JpaRepository
方法 | 描述 | SQL |
---|---|---|
List<T> findAll() | 参考 CrudRepository.findAll() | |
List<T> findAll(Sort) | 参考 PagingAndSortingRepository.findAll(Sort) | |
List<T> findAll(Iterable<ID<) | 参考 CrudRepository.findAll(Iterable<ID<) | |
List<S> save(Iterable<S>) | 参考 CrudRepository.save(Iterable<S>) | |
deleteInBatch(Iterable<T>) | 批量删除实体对象的记录 , 它只跟实体对象参数的 ID 属性值有关 |
DELETE ... WHERE ID = ? OR ID = ? ...
|
deleteAllInBatch() | 批量删除全部记录 |
DELETE FROM ...
|
数据访问接口编写
为使用基础的 CRUD
操作 ,
可继承 JpaRepository
,
不需要编写任何具体的实现 。
public interface ProductRepository extends JpaRepository<Product, Long>{ }
Application
@SpringBootApplication public class Application implements CommandLineRunner{ @Autowired private ProductRepository productRepository; public static void main(String[] args){ SpringApplication.run(Application.class); } @Override public void run(String... args) throws Exception{ /* ------------------------ 保存数据 ------------------------ */ productRepository.save(createProduct("深入理解Java虚拟机", "图书", 60.8, 100)); productRepository.save(createProduct("HotSpot实战", "图书", 50.3, 20)); productRepository.save(createProduct("Java并发编程实战", "图书", 44.6, 80)); productRepository.save(createProduct("Java多线程编程核心技术", "图书", 55.5, 0)); productRepository.save(createProduct("Effective Java中文版", "图书", 66.6, 30)); productRepository.save(createProduct("深入分析Java Web技术内幕", "图书", 77.7, 50)); productRepository.save(createProduct("大型网站技术架构核心原理与案例分析", "图书", 88.8, 0)); productRepository.save(createProduct("美的电饭煲", "家电", 279.0, 60)); productRepository.save(createProduct("小熊迷你养生壶全", "家电", 99.98, 0)); productRepository.save(createProduct("海尔三门冰箱", "家电", 358.10, 80)); /* ------------------------ 查询数据 ------------------------ */ productRepository.findAll().forEach(System.out::println); } private Product createProduct(String name, String category, Double price, Integer stock){ Product product = new Product(); product.setName(name); product.setPrice(price); product.setStock(stock); product.setCategory(category); product.setCreateTime(new Date()); return product; } }
创建查询方法
Spring Data JPA 提供的查询策略:
名称 | 描述 |
---|---|
CREATE | 查询创建策略 。 尝试从查询方法名称中构造一个特定的存储查询 。 其做法是从该查询方法名称中移除一组已知的特定前缀 , 并解析其余部分 |
USE_DECLARED_QUERY |
使用声明查询策略 。
尝试查找 @Query
注解标注的方法 ,
并使用指定的查询语句 |
CREATE_IF_NOT_FOUND |
默认使用的策略 。
它结合了 CREATE
和 USE_DECLARED_QUERY
策略 。
并且优先尝试使用 USE_DECLARED_QUERY
策略 ,
再尝试使用 CREATE
策略 |
查询创建策略
自定义的查询方法名称必须满足以下规则:
-
以下面罗列的前缀之一开始命名:
- findBy
- find…By
- readBy
- read…By
- queryBy
- query…By
- countBy
- count…By
- getBy
- get…By
-
第一个
By
之后添加查询的条件 , 可以使用特定的实体类的属性名称和支持的查询关键字(参考查询关键字表)来组合; -
如果要限制查询结果返回的数据的个数 ,
可以在第一个
By
之前添加First
或Top
关键字 , 此关键字后面还可以带数字 , 表示返回前多少条数据 , 如果数字省略 , 则表示返回第一条数据(如:findFirstByName(返回第1条) , findTopByName(返回第1条) , findTop10ByName(返回前10条)) , 当查询方法匹配到多个结果时 , 返回指定大小的结果或只返回第一条数据的结果; -
当需要查询唯一结果时 ,
在第一个
By
之前添加Distinct
关键字(如:findDistinctNameBy) - 如果查询方法指定了 N 个搜索条件 , 则此方法的签名也必须含有 N 个参数 , 即方法参数的数量必须等于搜索条件的数量 , 且 , 方法参数必须按搜索条件相同的顺序给出;
- 查询方法参数中 , 可以含有特殊的 Pageable 和 Sort 参数 , 用于分页和排序(注:此条款不受上一条款约束)
查询关键字表
关键字 | 示例 | JPQL |
---|---|---|
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 |
findByAgeNotNull findByAgeIsNotNull |
… 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<Age> ages) |
… where x.age in ?1
|
NotIn | findByAgeNotIn(Collection<Age> age) |
… 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)
|
查询返回值类型表
类型 | 描述 |
---|---|
void | 不需要返回值 |
Primitives | Java 基本数据类型 |
Wrapper | Java 基本数据类型对应的包装类型 |
T | 期望返回的实体数据类型 , 查询方法至多返回一条数据结果 , 多于一条数据的结果将抛出异常 , 没有查询到数据结果 , 则返回 null |
Iterator<T> | 迭代器类型 |
Collection<T> | 集合类型 |
List<T> | List 集合类型 |
Optional<T> | Java8 Optional 类型 |
Stream<T> | Java8 Stream 类型 |
Future<T> |
Java8 Future 类型 ,
使用 @Async
注解标注查询方法 ,
并且需要启用 Spring 异步方法执行的功能 |
CompletableFuture<T> |
Java8 CompletableFuture 类型 ,
使用 @Async
注解标注查询方法 ,
并且需要启用 Spring 异步方法执行的功能 |
ListenableFuture |
Spring ListenableFuture 类型 ,
使用 @Async
注解标注查询方法 ,
并且需要启用 Spring 异步方法执行的功能 |
Slice | 分页相关 |
Page<T> | 分页相关 |
@Query 创建查询
JPQL(Java Persistence Query Language , Java 持久化查询语言)和 SQL 之间有很多相似之处 , 它们之间主要的区别在于前者处理 JPA 实体(类名 –> 表名 , 属性 –> 列名) , 而后者则直接涉及关系数据 。
SQL:
@Query(value = "SELECT * FROM PRODUCT WHERE CATEGORY = ?", nativeQuery = true) List<Product> selectByCategory(String category);
JPQL:
@Query("select p from Product p where p.category = ?") List<Product> selectByCategory(String category);
更多 JPQL 的语法移步到: JPQL 语言语法
事务控制
第 17 行抛出异常 , 此时 p1 、 p2 已保存到数据库:
@Service public class ProductService{ @Autowired private ProductRepository productRepository; public void batchSave(){ Product p1 = new Product(); p1.setName("商品A"); Product p2 = new Product(); p2.setName("商品B"); Product p3 = new Product(); p3.setName("商品C"); productRepository.save(p1); productRepository.save(p2); // 模拟异常 p3.setStock(100 / 0); productRepository.save(p3); } }
通过使用 @Transactional
注解控制事务 ,
以下示例 p1 、
p2 、
p3 均未入库 ,
第 17 抛出异常导致数据回滚:
@Service public class ProductService{ @Autowired private ProductRepository productRepository; @Transactional public void batchSave(){ Product p1 = new Product(); p1.setName("商品A"); Product p2 = new Product(); p2.setName("商品B"); Product p3 = new Product(); p3.setName("商品C"); productRepository.save(p1); productRepository.save(p2); // 模拟异常 p3.setStock(100 / 0); productRepository.save(p3); } }
参考文档:
http://docs.spring.io/spring-data/jpa/docs/1.11.1.RELEASE/reference/html
以上所述就是小编给大家介绍的《Spring Boot JPA》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Spring Boot JPA
- Spring Boot 2和JPA入门
- 【快学springboot】7.使用Spring Boot Jpa
- Spring Boot 2.x(三):整合Spring Data JPA
- Spring Boot 最佳实践(五)Spring Data JPA 操作 MySQL 8
- Spring Boot + JPA实现MySQL批量更新源码 - github
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。