1.JPA简介
JPA(Java Persistence API)是Sun官方提出的Java持久化规范,它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。
Hibernate是JPA目前最流行的实现,JPA的制定很大程度上吸收了的思想,spring-boot-starter-data-jpa默认使用的Hibernate。
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 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
2.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 | 指定使用静态名称的查询。 |
3.代码示例
3.1. 工程结构
3.2. pom依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- JDK>=1.8 -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3.3. yml配置
# DataSource Config
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:test
username: root
password: test
jpa:
show-sql: true
hibernate:
ddl-auto: update
jpa.hibernate.show-sql是jpa通用配置,可以打印SQL,对于调试非常重要
jpa.hibernate.ddl-auto是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下:
- ·create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
- ·create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
- ·update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
- ·validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
3.4. 实体类(模型层)
package com.example.jpa.entity;
import lombok.Data;
import lombok.ToString;
import javax.persistence.*;
//必须注解,声明这个类是一个实体
@Entity
//可选注解,可以初始化建表信息,甚至完全定制建表SQL,默认的name(表名)即是类名
@Table(name = "USER")
@Data
@ToString
public class User {
//声明这个属性对应表中唯一标识(主键)字段
@Id
//可选,主键自增,默认GenerationType.AUTO
@GeneratedValue
private Long id;
//声明这个属性对应表中某个字段,其中所有配置属性都是可选的,但length默认为255,建议手动指定以配置一个合适的长度
@Column(name = "name", nullable = true, length = 20)
private String name;
@Column(name = "age", length = 3)
private Integer age;
@Column(name = "email", length = 50)
private String email;
}
3.5. 仓储类(持久层)
package com.example.jpa.repository;
import com.example.jpa.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
//必选注解,声明这是一个仓储
@Repository
//实现org.springframework.data.repository即使用了JPA
//org.springframework.data.jpa.repository.JpaRepository实现了一些常用数据库操作
//所以一般都选择继承JpaRepository以简化代码
public interface UserRepository extends JpaRepository<User,Long> {
//按照JPA规则自定义接口
public List<User> getByName(String name);
//自定义PQL
//nativeQuery=true则使用原生sql,更灵活,但失去了自动适应数据库产品的能力,一般不推荐
@Query(countQuery = "SELECT COUNT(U.AGE) FROM USER U WHERE U.AGE = :age")
public int countUsersByAge(@Param("age") int age);
}
JPQL规则常见关键字一览表
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | 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 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) |
3.6 测试类
package com.example.jpa;
import com.example.jpa.entity.User;
import com.example.jpa.repository.UserRepository;
import org.junit.Before;
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.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaApplicationTests {
@Autowired
UserRepository userRepository;
@Before
public void before(){
User user = new User();
user.setName("虫师");
user.setAge(123);
user.setEmail("xiaoyu@demo.com");
userRepository.save(user);
user = new User();
user.setName("泰密");
user.setAge(16);
user.setEmail("taimi@demo.com");
userRepository.save(user);
}
@Test
public void contextLoads() {
User user = new User();
user.setName("指挥官");
user.setAge(16);
user.setEmail("xiaohong@demo.com");
//spring提供的插入功能
User save = userRepository.save(user);
System.out.println("----插入-----\n"
.concat(save.toString())
.concat("\n----查询所有----"));
//spring提供的查询功能
List<User> all = userRepository.findAll();
all.forEach(System.out::println);
//自定义的查询功能
List<User> list = userRepository.getByName("虫师");
System.out.println("-----自定义接口------\n"
.concat(list.toString()));
//自定义计数功能
int i = userRepository.countUsersByAge(16);
System.out.println("-----自定义PQL------\n"
.concat(String.valueOf(i)));
}
}
3.7 控制台输出
Hibernate: call next value for hibernate_sequence
Hibernate: insert into user (age, email, name, id) values (?, ?, ?, ?)
Hibernate: call next value for hibernate_sequence
Hibernate: insert into user (age, email, name, id) values (?, ?, ?, ?)
Hibernate: call next value for hibernate_sequence
Hibernate: insert into user (age, email, name, id) values (?, ?, ?, ?)
----插入-----
User(id=3, name=指挥官, age=16, email=xiaohong@demo.com)
----查询所有----
2019-01-31 15:55:19.482 INFO 3144 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.name as name4_0_ from user user0_
User(id=1, name=虫师, age=123, email=xiaoyu@demo.com)
User(id=2, name=泰密, age=16, email=taimi@demo.com)
User(id=3, name=指挥官, age=16, email=xiaohong@demo.com)
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.email as email3_0_, user0_.name as name4_0_ from user user0_ where user0_.name=?
-----自定义接口------
[User(id=1, name=虫师, age=123, email=xiaoyu@demo.com)]
Hibernate: select count(user0_.id) as col_0_0_ from user user0_ where user0_.age=?
-----自定义PQL------
2