一、JPA介绍
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。
API用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。
二、JPA的优势
标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
容器级特性的支持
JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
简单方便
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易地掌握。JPA基于非侵入式原则设计,因此可以很容易地和其它框架或者容器集成。
查询能力
JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
高级特性
JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
三、SpringData JPA
SpringData JPA是Spring提供的一套简化JPA开发的框架,按照约定好的【方法命名规则】,就可以在不写接口实现的情况下,实现对数据库的访问和操作。提供了很多CRUD功能以及分页、排序、复杂查询等。
SpringData JPA可以理解为JPA规范的再次封装,底层默认才用的是Hibernate的JPA技术实现。
四、JPA方法命名规则
# | 关键词 | 示例 | 同功能SQL |
---|---|---|---|
1 | And | findByLastnameAndFirstname | where x.lastname = ? and x.firstname = ? |
2 | Or | findByLastnameOrFirstname | where x.lastname = ? or x.firstname = ? |
3 | Is,Equals | findByFirstname,findByFirstnameEquals | where x.firstname = ? |
4 | Between | findByStartDateBetween | where x.startDate between ? and ? |
5 | LessThan | findByAgeLessThan | where x.age < ? |
6 | LessThanEquals | findByAgeLessThanEquals | where x.age <= ? |
7 | GreaterThan | findByAgeGreaterThan | where x.age > ? |
8 | GreaterThanEquals | findByAgeGreaterThanEquals | where x.age >= ? |
9 | After | findByIdAfter | where x.Id > ? |
10 | Before | findByIdBefore | where x.Id < ? |
11 | IsNull | findByNameIsNull | where x.name is null |
12 | IsNotNull,NotNull | findByNameIsNotNull,findByNameNotNull | where x.name not null |
13 | Like | findByNameLike | where x.name like ? |
14 | NotLike | findByNameNotLike | where x.name not like ? |
15 | StartingWith | findByNameStartingWith | where x.name like ? |
16 | EndingWith | findByNameEndingWith | where x.name like ? |
17 | Containing | findByNameContaining | where x.name like ? |
18 | OrderBy | findByAgeOrderByNameDesc | where x.age = ? order by name desc |
19 | Not | findByNameNot | where x.name <> ? |
20 | In | findByAgeIn(Collection<Age> ages) | where x.age in ? |
21 | NotIn | findByAgeNotIn(Collection<Age> ages) | where x.age not in ? |
22 | True | findByActiveTrue | where x.active = true |
23 | False | findByActiveFalse | where x.active = false |
24 | IgnoreCase | findByNameIgnoreCase | where UPPER(x.name) = UPPER(?) |
组合查询示例:
在UserRepository接口中findByAgeOrderByNameDesc(Integer age); 首先findByAge查询年龄=age,再通过OrderBy排序,排序条件为Name降序,生成的SQL语句即为:SELECT * FROM User WHERE Age = ? ORDER BY Name DESC
五、JPA注解
注解 解释
注解 | 解释 |
---|---|
@Entity | 声明类为实体或表 |
@Table | 声明表名 |
@Basic | 指定非约束明确的各个字段 |
@Embedded | 指定类或它的值是一个可嵌入的类的实例的实体的属性 |
@Id | 指定的类的属性,用于识别(一个表中的主键) |
@GeneratedValue | 指定如何标识属性可以被初始化,例如自动、手动、或从序列表中获得的值 |
@Transient | 指定的属性,它是不持久的,即:该值永远不会存储在数据库中 |
@Column | 指定持久属性栏属性 |
@SequenceGenerator | 指定在@GeneratedValue注解中指定的属性的值。它创建了一个序列 |
@TableGenerator | 指定在@GeneratedValue批注指定属性的值发生器。它创造了值生成的表 |
@AccessType | 这种类型的注释用于设置访问类型。如果设置@AccessType(FIELD),则可以直接访问变量并且不需要getter和setter,但必须为public。如果设置@AccessType(PROPERTY),通过getter和setter方法访问Entity的变量 |
@JoinColumn | 指定一个实体组织或实体的集合。这是用在多对一和一对多关联 |
@UniqueConstraint | 指定的字段和用于主要或辅助表的唯一约束 |
@ColumnResult | 参考使用select子句的SQL查询中的列名 |
@ManyToMany | 定义了连接表之间的多对多一对多的关系 |
@ManyToOne | 定义了连接表之间的多对一的关系 |
@OneToMany | 定义了连接表之间存在一个一对多的关系 |
@OneToOne | 定义了连接表之间有一个一对一的关系 |
@NamedQueries | 指定命名查询的列表 |
@NamedQuery | 指定使用静态名称的查询 |
六、SpringBoot整合JPA
1、pom.xml 添加依赖
<!-- spring jpa --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
2、创建实体表
package com.demo.springboot.entity; import lombok.Data; import javax.persistence.*; @Data @Entity //告诉JPA这是一个实体类(和数据表映射的类) @Table(name = "t_user") //@Table来指定和哪个数据表对应;如果省略默认表名就是user; public class User { @Id //这是一个主键 @GeneratedValue(strategy = GenerationType.IDENTITY) //自增主键 private Integer id; @Column(name = "last_name", length = 50) //这是和数据表对应的一个列 private String lastName; @Column //省略默认列名就是属性名 private String email; }
3、创建UserRepository
package com.demo.springboot.repository; import com.demo.springboot.entity.User; import org.springframework.data.jpa.repository.JpaRepository; /** * 继承JpaRepository来完成对数据库的操作 * JpaRepository 继承了CrudRepository、PagingAndSortingRepository、QueryByExampleExecutor * 实现了常用的增删改查、分页、排序等功能 */ public interface UserRepository extends JpaRepository<User, Integer> { User findByLastName(String lastName); // 根据JPA命名规则,不需要实现即可使用 User findByEmail(String email); }
4、创建UserController
package com.demo.springboot.controller; import com.demo.springboot.entity.User; import com.demo.springboot.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired UserRepository userRepository; @PostMapping("/user") public User insertUser(User user) { // 使用save方法进行新增 User save = userRepository.save(user); return save; } @GetMapping("/user/id/{id}") public User getUser(@PathVariable("id") Integer id) { User user = userRepository.findOne(id); return user; } @GetMapping("/user/name/{lastName}") public User getUserByLastName(@PathVariable("lastName") String lastName) { // 调用自定义的findByLastName接口,不需要自己实现 User user = userRepository.findByLastName(lastName); return user; } @GetMapping("/user/email/{email}") public User getUserByEmail(@PathVariable("email") String email) { User user = userRepository.findByEmail(email); return user; } }
访问接口时,控制台打印出来的SQL依次为:
Hibernate: insert into t_user (email, last_name) values (?, ?)
Hibernate: select user0_.id as id1_0_0_, user0_.email as email2_0_0_, user0_.last_name as last_nam3_0_0_ from t_user user0_ where user0_.id=?
2021-09-05 10:17:29.955 INFO 11692 --- [nio-8080-exec-8] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_0_, user0_.email as email2_0_, user0_.last_name as last_nam3_0_ from t_user user0_ where user0_.last_name=?
Hibernate: select user0_.id as id1_0_, user0_.email as email2_0_, user0_.last_name as last_nam3_0_ from t_user user0_ where user0_.email=?
七、参考文档
https://baike.baidu.com/item/JPA/5660672?fr=aladdin
https://blog.csdn.net/wujiaqi0921/article/details/78789087
https://www.cnblogs.com/fengzheng/p/11531565.html