JPA概述
JPA:(Java Persistence API)Java持久化API。是sun公司提出的一套ORM规范。
JPA:其实是一套规范,而非框架。因为 JPA 并未提供 ORM 实现,它只是制订了一些规范,提供了一些编程的 API 接口,但具体实现则由 ORM 厂商提供实现。 Hibernate框架中提供了实现了JPA的实现。
JPA:通过JDK5.0注解或XML,描述对象——关系表的映射关系。并将运行时的对象持久化到数据库。
Hibernate有自己独立的ORM操作数据库方式,也有实现了JPA规范的操作数据库的方式。两种方式我们都要掌握
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 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。****
JPA入门
环境搭建
创建项目,导入相关jar包
需要hibernate必须jar包,数据库驱动包,连接池包,jpa相关jar包
编写配置文件
1.在src(根目录下)创建一个META-INF文件夹
2.在META-INF文件夹下,创建一个persistence.xml文件
3.编写配置
引入schema约束(jpa的jar包中的persistence_2_1.xsd文件中寻找)
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> ... </persistence>
根标签:
配置持久化单元,可以配置多个,但是名称不能重复 name:用于指定持久化单元名称 transaction-type:指定事务的类型。 JTA:Java Transaction API(多用于集群事务) RESOURCE_LOCAL:指的是本地数据库事务。(我们用这个)
persistence-unit中包括三部分
- org.hibernate.jpa.HibernatePersistenceProvider
<properties> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/jpa_demo?serverTimezone=GMT%2B8"/> <property name="username" value="root"/> <property name="password" value="123456"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/><!-- 数据库的方言 --> <!-- 第二部分:hibernate的可选配置 --> <property name="hibernate.connection.provider_class" value="com.alibaba.druid.support.hibernate.DruidConnectionProvider" /> <!-- 是否显示hibernate生成的SQL语句 --> <property name="hibernate.show_sql" value="true"/> <!-- 是否使用格式化输出sql语句到控制台 --> <property name="hibernate.format_sql" value="true"/> <!-- 配置hibernate采用何种方式生成DDL语句 --> <property name="hibernate.hbm2ddl.auto" value="update"/><!-- update表示检测实体类的映射配置和数据库的表结构是否一致,如果不一致,更新表结构 --> </properties>
编写工具类
用于获取操作数据库的对象EntityManager
package com.fubo.util;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JPAUtil {
//该类相对于我们学的SessionFactory
private static EntityManagerFactory factory;
static{
factory = Persistence.createEntityManagerFactory("mysql_persistence");
}
public static EntityManager getEntityManager(){
EntityManager entityManager = factory.createEntityManager();
return entityManager;
}
@Test
public void test(){
getEntityManager();
}
}
编写实体类
package com.fubo.entity;
import javax.persistence.*;
@Entity
@Table(name="db_student")
public class DbStudent {
@Id//表明主键对应的属性
@Column(name="id")//表明对应的数据库字段名称为id
/**指定主键生成策略,
* strategy:使用jpa中提供的主键生成策略(hibernate的生成策略,此属性用不了),
* generator可以使用hibernate的主键生成策略。
*/
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
@Column(name = "stu_no")
private String stuNo;
private String status;
//getter/setter省略
}
持久化类中注解详解
@Entity
- 指定当前类是持久化类。
- 写上此注解用于在创建SessionFactory/EntityManagerFactory时,加载映射配置到内存中。
@Table
指定实体和表的关系,并且可以指定数据库的表面
属性
name: 指定数据库表名
@Id
- 指定当前属性所对应的数据库字段拥有主键
@GeneratedValue
指定主键的生成策略。它支持jpa生成策略和Hibernate提供的生成策略
- 使用jpa生成策略,需要设置属性 strategy
- 使用hibernate生成策略,需要设置属性generator
jpa支持的生成策略:
- IDENTITY:主键由数据库自动生成(主要是自动增长型)。
- SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
- AUTO:主键由程序控制。
- TABLE:使用一个特定的数据库表格来保存主键(了解)。
@Column
指定实体属性对应的数据库的表的列。如果不加该注解的属性,默认对应的列名为属性名称。
属性
name:指定属性对应的字段名称
unique:指定属性对应的字段是否拥有唯一约束,默认false
nullable:指定属性对应的字段是否可以为null。默认为true
JPA之C
@Test
public void test1(){
DbStudent student = new DbStudent();
student.setUsername("西门庆");
student.setStuNo("xmq");
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(student);
transaction.commit();
entityManager.close();
}
JPA之R
####立即加载获取实体
@Test
public void test2(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
DbStudent student = entityManager.find(DbStudent.class, 1);
System.out.println(student);
transaction.commit();
entityManager.close();
}
####延迟加载获取实体
@Test
public void test22(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
DbStudent student = entityManager.getReference(DbStudent.class,1);
System.out.println(student);
transaction.commit();
entityManager.close();
}
JPA之U
方法一,通过快照机制实现自动修改
@Test
public void test3(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
DbStudent student = entityManager.find(DbStudent.class, 1);
student.setUsername("武大郎威武");
transaction.commit();
entityManager.close();
}
方法二,通过merge方法
**
* 修改(二)
*/
@Test
public void test33() {
//1.获取数据库操作对象EntityManager
EntityManager em = JPAUtil.getEntityManager();
//2.获取事务对象,并开启事务
EntityTransaction tx =em.getTransaction();
tx.begin();
//3.执行操作
DbStudent student = em.find(DbStudent.class, 1);
student.setRealName("付波");
em.merge(student);
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
JPA之D
@Test
public void test4(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
DbStudent student = entityManager.find(DbStudent.class, 1);
entityManager.remove(student);
transaction.commit();
entityManager.close();
}
JPA之R二
@Test
public void test5(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//获取JPA的Query对象,通过EntityManager.createQuery(String jpql)
//jpql:java persistence query language。
//JPQL与hql很相似,都是把表名改为实体名称,字段名该为实体属性名称。
//唯一的不同点:查询所有时,不能直接使用from 。使用select 实体别名 from 实体 实体别名
String jpql ="select s from DbStudent s where id>:id";
Query query = entityManager.createQuery(jpql);
query.setParameter("id",1);
List<DbStudent> resultList = query.getResultList();
for (DbStudent student : resultList) {
System.out.println(student);
}
transaction.commit();
entityManager.close();
}
JPA中一对多的关系映射
设计的注解
@OneToMany 在一方进行配置即主表中进行配置
属性:
targetEntityClass:指定多的一方的类名
mappedBy:指定多的一方的引用一的一方的对象引用名称
cascade:设置级联操作的
fetch:设置延迟加载
@ManyToOne 在多方进行配置即从表中配置
属性:
targetEntityClass:指定一的一方的类名
cascade:设置级联操作
fetch:设置延迟加载
@JoinColumn在多方进行配置即从表中配置
属性:
name:设置表的外键名称
referencedColumnName:设置引用主表的主键字段名称
unique:是否唯一。默认值不唯一
nullable:是否允许为空。默认值允许。
一的一方实体配置
package com.fubo.entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name="stu_class")
public class StuClass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String className;
private String classNo;
/**
* @OneToMany
* 作用:配置一对多的映射关系
* 属性:
* targetEntityClass:指定多的一方的类的名称
* mappedBy:指定从表实体类中引用主表对象的名称。
* cascade:指定要使用的级联操作
* fetch:指定是否采用延迟加载
*/
@OneToMany(targetEntity = DbStudent.class,mappedBy = "stuClass")
private Set<DbStudent> students = new HashSet<DbStudent>(0);
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getClassNo() {
return classNo;
}
public void setClassNo(String classNo) {
this.classNo = classNo;
}
public Set<DbStudent> getStudents() {
return students;
}
public void setStudents(Set<DbStudent> students) {
this.students = students;
}
}
多的一方实体配置
package com.fubo.entity;
import javax.persistence.*;
@Entity
@Table(name="db_student")
public class DbStudent {
@Id//表明主键对应的属性
@Column(name="id")//表明对应的数据库字段名称为id
/**指定主键生成策略,
* strategy:使用jpa中提供的主键生成策略(hibernate的生成策略,此属性用不了),
* generator可以使用hibernate的主键生成策略。
*/
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
@Column(name = "stu_no" )
private String stuNo;
private String status;
@ManyToOne(targetEntity = StuClass.class)
@JoinColumn(name = "stu_class_id",referencedColumnName = "id")
private StuClass stuClass;
public StuClass getStuClass() {
return stuClass;
}
public void setStuClass(StuClass stuClass) {
this.stuClass = stuClass;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "DbStudent{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", stuNo='" + stuNo + '\'' +
", status='" + status + '\'' +
'}';
}
}
JPA一对多操作之C
@Test
public void testSave(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
StuClass stuClass = new StuClass();
stuClass.setClassName("西游记");
stuClass.setClassNo("xiyouji_001");
DbStudent student = new DbStudent();
student.setUsername("猪八戒");
student.setStuNo("xyj_003");
//维护双方的关系
student.setStuClass(stuClass);
stuClass.getStudents().add(student);
//保存
entityManager.persist(stuClass);
entityManager.persist(student);
transaction.commit();
entityManager.close();
}
@Test
public void testSave2(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
StuClass stuClass = new StuClass();
stuClass.setClassName("水浒传");
stuClass.setClassNo("shuihuzhuan_001");
DbStudent student = new DbStudent();
student.setUsername("阮小二");
student.setStuNo("shuihuizhuan_003");
//维护双方的关系
student.setStuClass(stuClass);
stuClass.getStudents().add(student);
//保存
entityManager.persist(stuClass);
// entityManager.persist(student);
transaction.commit();
entityManager.close();
}
JPA一对多操作之U
我们主要关注级联保存,级联保存需要维护双方关系
@Test
public void testUpdateCascade(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
DbStudent student = new DbStudent();
student.setUsername("阮小五");
StuClass stuClass = entityManager.find(StuClass.class, 2);
student.setStuClass(stuClass);
stuClass.getStudents().add(student);
entityManager.merge(stuClass);
transaction.commit();
entityManager.close();
}
@Entity
@Table(name="stu_class")
public class StuClass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String className;
private String classNo;
/**
* @OneToMany
* 作用:配置一对多的映射关系
* 属性:
* targetEntityClass:指定多的一方的类的名称
* mappedBy:指定从表实体类中引用主表对象的名称。
* cascade:指定要使用的级联操作
* fetch:指定是否采用延迟加载
*/
@OneToMany(targetEntity = DbStudent.class,mappedBy = "stuClass",cascade = CascadeType.MERGE)
private Set<DbStudent> students = new HashSet<DbStudent>(0);
}
JPA一对多操作之D
删除主表,级联删除从表数据
@Test
public void testDelete(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
StuClass stuClass = entityManager.find(StuClass.class, 2);
entityManager.remove(stuClass);
transaction.commit();
entityManager.close();
}
@OneToMany(targetEntity = DbStudent.class,mappedBy = "stuClass",cascade = {CascadeType.MERGE,CascadeType.REMOVE})
private Set<DbStudent> students = new HashSet<DbStudent>(0);
JPA一对多操作之R二
###导航查询主表中查从表数据
- 导航查询之主表获取从表数据,默认是延迟加载
- 可以设置为立即查找,需要设置fetch=FetchType.EAGRE
@OneToMany(targetEntity = DbStudent.class,mappedBy = "stuClass",cascade = {CascadeType.MERGE,CascadeType.REMOVE} ,fetch = FetchType.EAGER)
private Set<DbStudent> students = new HashSet<DbStudent>(0);
@Test
public void testQueryByNav(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
StuClass stuClass = entityManager.find(StuClass.class, 1);
System.out.println(stuClass);
Set<DbStudent> students = stuClass.getStudents();
System.out.println(students);
transaction.commit();
entityManager.close();
}
导航查询从表中查主表数据
- 默认是立即加载
- 可以设置为延迟加载,需要设置fetch=FetchType.LAZY
@ManyToOne(targetEntity = StuClass.class,fetch = FetchType.LAZY)
@JoinColumn(name = "stu_class_id",referencedColumnName = "id")
private StuClass stuClass;
@Test
public void testQueryByNav2(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
DbStudent student = entityManager.find(DbStudent.class, 1);
System.out.println(student);
StuClass stuClass = student.getStuClass();
System.out.println(stuClass);
transaction.commit();
entityManager.close();
}
JPA中多对多关系映射
多方的实体StuRole配置
@Entity
@Table(name="stu_role")
public class StuRole {
@Id
@GenericGenerator(name="my",strategy = "native")
@GeneratedValue(generator ="my")
private Integer id;
private String roleName;
@ManyToMany(mappedBy = "roles")
private Set<DbStudent> students = new HashSet<DbStudent>(0);
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public Set<DbStudent> getStudents() {
return students;
}
public void setStudents(Set<DbStudent> students) {
this.students = students;
}
}
多方实体DbStudent配置
package com.fubo.entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name="db_student")
public class DbStudent {
@Id//表明主键对应的属性
@Column(name="id")//表明对应的数据库字段名称为id
/**指定主键生成策略,
* strategy:使用jpa中提供的主键生成策略(hibernate的生成策略,此属性用不了),
* generator可以使用hibernate的主键生成策略。
*/
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
@Column(name = "stu_no" )
private String stuNo;
private String status;
@ManyToOne(targetEntity = StuClass.class,fetch = FetchType.LAZY)
@JoinColumn(name = "stu_class_id",referencedColumnName = "id")
private StuClass stuClass;
@ManyToMany
@JoinTable(name="stu_role_ref",joinColumns = {@JoinColumn(name="stu_id",referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name="role_id",referencedColumnName = "id")})
private Set<StuRole> roles = new HashSet<StuRole>(0);
public StuClass getStuClass() {
return stuClass;
}
public void setStuClass(StuClass stuClass) {
this.stuClass = stuClass;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "DbStudent{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", stuNo='" + stuNo + '\'' +
", status='" + status + '\'' +
'}';
}
public Set<StuRole> getRoles() {
return roles;
}
public void setRoles(Set<StuRole> roles) {
this.roles = roles;
}
}
JPA多对多操作之C
级联保存,同时保存两个对象数据。
@Test
public void testSaveCascade(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
StuRole stuRole = new StuRole();
stuRole.setRoleName("美女");
DbStudent student = new DbStudent();
student.setUsername("林心如");
student.getRoles().add(stuRole);
stuRole.getStudents().add(student);
entityManager.persist(stuRole);
transaction.commit();
entityManager.close();
}
", status='" + status + '\'' +
'}';
}
public Set<StuRole> getRoles() {
return roles;
}
public void setRoles(Set<StuRole> roles) {
this.roles = roles;
}
}
## JPA多对多操作之C
级联保存,同时保存两个对象数据。
```java
@Test
public void testSaveCascade(){
EntityManager entityManager = JPAUtil.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
StuRole stuRole = new StuRole();
stuRole.setRoleName("美女");
DbStudent student = new DbStudent();
student.setUsername("林心如");
student.getRoles().add(stuRole);
stuRole.getStudents().add(student);
entityManager.persist(stuRole);
transaction.commit();
entityManager.close();
}