JPA概述
JPA是什么
JPA (Java Persistence API)Java持久化API。是一套Sun公司Java官方制定的ORM 方案,是规范,是标准 ,sun公司自己并没有实现
关注点:ORM ,标准 概念 (关键字)
ORM是什么
ORM(Object Relational Mapping)对象关系映射。
问:ORM有什么用?
在操作数据库之前,先把数据表与实体类关联起来。然后通过实体类的对象操作(增删改查)数据库表,这个就是ORM的行为!
所以:ORM是一个实现使用对象操作数据库的设计思想!!!
通过这句话,我们知道JPA的作用就是通过对象操作数据库的,不用编写sql语句。
JPA的实现者
既然我们说JPA是一套标准,意味着,它只是一套实现ORM理论的接口。没有实现的代码。
那么我们必须要有具体的实现者才可以完成ORM操作功能的实现!
市场上的主流的JPA框架(实现者)有:
Hibernate (JBoos)、EclipseTop(Eclipse社区)、OpenJPA (Apache基金会)。
其中Hibernate是众多实现者之中,性能最好的。所以,我们本次教学也是选用Hibernate框架作为JPA的主讲框架。
提醒:学习一个JPA框架,其他的框架都是一样使用
JPA的作用是什么(问题)
JPA是ORM的一套标准,既然JPA为ORM而生,那么JPA的作用就是实现使用对象操作数据库,不用写SQL!!!.
问题:数据库是用sql操作的,那用对象操作,由谁来产生SQL?
答:JPA实现框架
入门示例
任何框架的学习,都建议从配置流程图开始。所以我们来一起理解JPA的配置流程图。
配置流程图
1. 我们需要一个总配置文件persistence.xml存储框架需要的信息 (注意,文件名不要写错,而且必须放在classpath/META-INF文件夹里面)
2. 我们需要一个Persistence持久类对象来读取总配置文件,创建实体管理工厂对象
3. 我们需要实体管理工厂获得数据库的操作对象实体管理对象EntityManager。
4. 我们通过EntityManager操作数据库之前,必须要先配置表与实体类的映射关系,从而实现使用对象操作数据库!!!
配置步骤说明
第一步:导入包 (不管什么框架,首先要做的事情)
第二步:创建一个总配置文件
第三步:创建一个JPAUtils获得操作对象EntityManager
第四步:创建一个实体类,并且配置好映射注解
第五步:在总配置文件加载实体类
第六步:测试代码(需求:插入数据到用户表)
配置步骤
需求:编写一个JPA的项目,插入一条数据到学生信息表。
第一步:创建Maven项目
说明:我们这里是基于hibernate实现的,所以要导入Hibernate的JPA规范包
--使用maven构建的配置--
-->
第二步:创建一个总配置文件
注意:文件必须放在classpath:/META-INF/persistence.xml
说明:Eclipse已经支持了JPA框架,所有不需要配置xsd文件,直接使用
配置信息如下:
"1.0" encoding="UTF-8"?>
第三步:封装JPAUtils工具类
创建一个工具类JPAUtils,获得操作对象(EntityManager)
package cn.zj.jpa.util;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JPAUtils {
//同一个应用中,应该保证只有一个实例工厂。
public static EntityManagerFactory emf = createEntityManagerFactory();
//1.获得实体管理工厂
private static EntityManagerFactory createEntityManagerFactory(){
EntityManagerFactory emf = Persistence.createEntityManagerFactory("mysql-jpa");
return emf;
}
//2.获得实体管理类对象
public static EntityManager getEntityManger(){
EntityManager entityManager = emf.createEntityManager();
return entityManager;
}
}
第四步:创建映射实体类
创建一个映射的实体类,将JPA的映射注解写在实体类里面。
package cn.zj.jpa.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
//1.指定实体类与表名的关系
//@Entity注解,指定该实体类是一个基于JPA规范的实体类
@Entity
//@Table注解,指定当前实体类关联的表
@Table(name="tb_student")
public class Student {
//@Id注解:声明属性为一个OID属性
@Id
//@GeneratedValue注解,指定主键生成策略
@GeneratedValue(strategy=GenerationType.IDENTITY)
//@Column注解,设置属性与数据库字段的关系,如果属性名和表的字段名相同,可以不设置
@Column(name="stu_id")
private Long stuId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '学生编号',
@Column(name="stu_name")
private String stuName;//VARCHAR(50) NULL DEFAULT NULL COMMENT '学生名字',
@Column(name="stu_age")
private Integer stuAge;//INT(11) NULL DEFAULT NULL COMMENT '学生年龄',
@Column(name="stu_password")
private String stuPassword;//VARCHAR(50) NULL DEFAULT NULL COMMENT '登录密码',
public Student() {
super();
}
//补全get、set方法
}
第五步:在总配置文件中加载映射实体类
"1.0" encoding="UTF-8"?>
-->
基于hibernate框架的JPA已经实现了自动载入映射实体类 ,所以不配置也是可以的。建议还是加上配置。如果不写容易忽略加载的实体类有哪些
-->
第六步:操作实体类保存数据
创建一个StudentDAOTest类,测试保存一个学生。
package cn.zj.jpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import org.junit.Test;
import cn.zj.jpa.entity.Student;
import cn.zj.jpa.util.JPAUtils;
public class StudentDAOTest {
@Test
public void persist(){
//1.获得实体管理类
EntityManager manager = JPAUtils.getEntityManger();
//2、获取事物管理器
EntityTransaction transaction = manager.getTransaction();
transaction.begin();
//3、创建实体对象
Student s=new Student();
s.setStuName("张三");
s.setStuAge(18);
s.setStuPassword("zj");
//4、保存到数据库
manager.persist(s);
//5、提交事物
transaction.commit();
//6、关闭资源
manager.close();
}
}
通过操作实体对象保存数据成功!!!
使用JPA的好处
使用JPA,可以直接使用对象操作数据库,由框架根据映射的关系生成SQL。不用开发人员编写。这样做,开发人员就不用编写SQL语句了。
问题:这样有什么好处呢?
答:不同的数据库的SQL语法是有差异,如果不需要编写SQL语句。就屏蔽各种数据库SQL的差异。那么,编写的代码就可以一套代码兼容多种数据库!!!!
JPA实现CRUD
修改StudentDAOTest类,测试crud操作
//通过OID删除
@Test
public void remove(){
//1.获得实体管理类对象
EntityManager entityManager = JPAUtils.getEntityManger();
//2.打开事务
EntityTransaction transaction = entityManager.getTransaction();
//3.启动事务
transaction.begin();
//4.创建数据,删除数据必须使用持久化对象
Student s=entityManager.find(Student.class, 2L);
//5.插入
entityManager.remove(s);;
//6。提交
transaction.commit();
//7.关闭
entityManager.close();
}
//更新
@Test
public void merge(){
//1.获得实体管理类对象
EntityManager entityManager = JPAUtils.getEntityManger();
//2.打开事务
EntityTransaction transaction = entityManager.getTransaction();
//3.启动事务
transaction.begin();
//4.创建数据
Student s=new Student();
s.setStuName("李四");
//更新必须要有一个OID
s.setStuId(3L);
//5.更新
entityManager.merge(s);
//6。提交
transaction.commit();
//7.关闭
entityManager.close();
}
//通过OID获得数据
@Test
public void find(){
//1.获得实体管理类对象
EntityManager entityManager = JPAUtils.getEntityManger();
//通过OID查询数据
Student student = entityManager.find(Student.class, 1L);
System.out.println(student.getStuName());
entityManager.close();
}
//通过OID获得数据
@Test
public void getReference(){
//1.获得实体管理类对象
EntityManager entityManager = JPAUtils.getEntityManger();
/**
* getReference()和find()方法的区别:
* getReference基于懒加载机制,即需要使用对象的时候,才执行查询。
*/
Student student = entityManager.getReference(Student.class, 1L);
System.out.println(student.getStuName());
entityManager.close();
}
JPA常用 API说明
映射注解说明
注解说明
@Entity声明该实体类是一个JPA标准的实体类
@Table指定实体类关联的表,注意如果不写表名,默认使用类名对应表名。
@Column指定实体类属性对应的表字段,如果属性和字段一致,可以不写
@Id声明属性是一个OID,对应的一定是数据库的主键字段
@GenerateValue声明属性(Object ID)的主键生成策略
@SequenceGenerate使用SEQUENCE策略时,用于设置策略的参数
@TableGenerate使用TABLE主键策略时,用于设置策略的参数
@JoinTable关联查询时,表与表是多对多的关系时,指定多对多关联表中间表的参数。
@JoinColumn关联查询时,表与表是一对一、一对多、多对一以及多对多的关系时,声明表关联的外键字段作为连接表的条件。必须配合关联表的注解一起使用
@OneToMany关联表注解,表示对应的实体和本类是一对多的关系
@ManyToOne关联表注解,表示对应的实体和本类是多对一的关系
@ManyToMany关联表注解,表示对应的实体和本类是多对多的关系
@OneToOne关联表注解,表示对应的实体和本类是一对一的关系
JPA常用API说明
API说明
Persistence用于读取配置文件,获得实体管理工厂
EntityManagerFactory用于管理数据库的连接,获得操作对象实体管理类
EntityManager实体管理类,用于操作数据库表,操作对象
EntityTransaction用于管理事务。开始,提交,回滚
TypeQuery用于操作JPQL的查询的
Query用于操作JPQL的查询接口,执行没有返回数据的JPQL(增删改)
CriteriaBuilder用户使用标准查询接口 Criteria查询接口
JPA多表关联查询
多个关联查询作用(导航查询):就是实现使用一个实体类对象查询多个表的数据。
配置多表联系查询必须有两个步骤;
(1)、在实体类里面建立表与表之间的关系。
(2)、在实体类配置关联关系,JPA使用注解配置
多表关联的E-R图如下:
根据ER图,创建数据库表!!!
一对多实现(单向)
需求:通过ID查询一条学生表的记录,同时查询该学生的对应的成绩的信息!
说明
如图所示:一个学生可以有多条成绩的记录,一条成绩的记录只属于一个学生,所以学生表与成绩表的关系是一对多的关系。
所以,通过JPA配置一对多的关系,可以通过学生表对应的实体类对象同时获得两个表的数据。
配置步骤
第一步:创建项目
说明:复制入门示例的项目即可。
第二步:创建单表实体类
(1)创建Student类
@Entity
@Table(name="tb_student")
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="stu_id")
private Long stuId;
@Column(name="stu_name")
private String stuName;
@Column(name="stu_age")
private Integer stuAge;
@Column(name="stu_password")
private String stuPassword;
public Student() {
super();
}
//补全get、set方法
}
(2)创建Score类
@Entity
@Table(name="tb_score")
public class Score{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="sco_id")
private Long scoId;
@Column(name="sco_subject")
private String scoSubject;
@Column(name="sco_score")
private Float scoScore;
@Column(name="stu_id")
private Long stuId;
public Score() {
super();
}
// 补全get、set方法
}
第三步:配置一对多关联关系
说明:通过@OneToMany注解配置。
修改Student类,配置一对多关系。
/**
* 单向一对对,应该有学生来维护关系
*
* 一个学生对应多个成绩,一对多关系
* 多个成绩我们使用list封装起来
*
* JPA 使用 @OneToMany 映射一对多
* fetch : 抓取策略
* FetchType.LAZY 懒加载,默认 (只有关联对象在用到的时候才会去发送新的sql,默认关联对象不会查询)
* 会多生成sql语句 :N+1
* FetchType.EAGER 迫切查询 (多表连接查询,只会发送一条sql语句)
* @JoinColumn 设置两张表之间外键列
*/
@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name="stu_id")
private List
public void setScores(List
this.scores = scores;
}
第四步:测试一对多查询
@Test
public void testOne2Many(){
//1.获得实体管理类
EntityManager manager = JPAUtils.getEntityManger();
Student student = manager.find(Student.class, 1L);
System.out.println("学生id:"+student.getStuId()+",学生姓名:"+student.getStuName());
List
for (Score score : scores) {
System.out.println("科目:"+score.getScoSubject()+",分数:"+score.getScoScore());
}
//6、关闭资源
manager.close();
}
查询结果:
一对多关联查询成功!!!
多对一实现(单向)
说明
需求:通过ID查询一条成绩表的记录,同时查询该成绩的对应的学生的信息!
如图所示:成绩表里面,每一条记录只能对应一个学生,但是学生编号不是唯一的。所以成绩表里面的多条数据可以对应一个学生,所以我们称多对一的关系。
配置步骤
第一步:创建项目
复制一对多示例项目即可。
第二步:创建单表实体类
修改Student类,去掉一对多配置即可。
第三步:配置多对一关联关系
修改Score类,配置多对一关系
@Entity
@Table(name="tb_score")
public class Score{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="sco_id")
private Long scoId;
@Column(name="sco_subject")
private String scoSubject;
@Column(name="sco_score")
private Float scoScore;
/**
* jpa的多对一,关联关系中指定的外键 和 关联表的属性有冲突
*解决的方案:去掉关联表中外键对应的属性
/*@Column(name="stu_id")
private Long stuId;
public Long getStuId() {
return stuId;
}
public void setStuId(Long stuId) {
this.stuId = stuId;
}
*/
/**
* 1、分数和学生信息是多对一的关系
* 2、只需要一个学生的实体来引用学生的信息
*/
@ManyToOne
@JoinColumn(name="stu_id")
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
//补全get、set方法
}
第四步:测试
@Test
public void testMany2One(){
//1.获得实体管理类
EntityManager manager = JPAUtils.getEntityManger();
Score score = manager.find(Score.class, 1L);
System.out.println("科目:"+score.getScoSubject()+",分数:"+score.getScoScore());
Student student = score.getStudent();
System.out.println("学生id:"+student.getStuId()+",学生姓名:"+student.getStuName());
//6、关闭资源
manager.close();
}