JPA

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构建的配置-- 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.zj</groupId>

  <artifactId>jpa-demo01-start</artifactId>

  <version>0.0.1-SNAPSHOT</version>


  <dependencies>

    <!-- hibernate框架 实现JPA 依赖 -->

<dependency>

    <groupId>org.hibernate</groupId>

    <artifactId>hibernate-entitymanager</artifactId>

    <version>4.3.6.Final</version>

</dependency>

-->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.40</version>

</dependency>

  </dependencies>

</project>


第二步创建一个总配置文件

注意:文件必须放在classpath:/META-INF/persistence.xml 

 

说明:Eclipse已经支持了JPA框架,所有不需要配置xsd文件,直接使用


配置信息如下:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd ">

  <persistence-unit name="mysql-jpa">


<properties> 


<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> 

<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/jpa" /> 

<property name="hibernate.connection.username" value="root" /> 

<property name="hibernate.connection.password" value="root" /> 

<!--可选配置--> 

<!--控制台打印sql语句--> 

<property name="hibernate.show_sql" value="true" /> 


<property name="hibernate.format_sql" value="true" /> 

</properties> 

  </persistence-unit>

</persistence>


第三步:封装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方法

}


第五步:在总配置文件中加载映射实体类

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd ">

  <persistence-unit name="mysql-jpa">


   -->

   基于hibernate框架的JPA已经实现了自动载入映射实体类 ,所以不配置也是可以的。建议还是加上配置。如果不写容易忽略加载的实体类有哪些

     -->

    <class>cn.zj.jpa.entity.Student</class>


<properties> 


<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> 

<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/jpa" /> 

<property name="hibernate.connection.username" value="root" /> 

<property name="hibernate.connection.password" value="zj" /> 

<!--可选配置--> 

<!--控制台打印sql语句--> 

<property name="hibernate.show_sql" value="true" /> 


<property name="hibernate.format_sql" value="true" /> 

</properties> 

  </persistence-unit>

</persistence>


第六步:操作实体类保存数据

创建一个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关联查询时,表与表是一对一、一对多、多对一以及多对多的关系时,声明表关联的外键字段作为连接表的条件。必须配合关联表的注解一起使用 <key>

@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();

}

// 补全getset方法

}


第三步:配置一对多关联关系

说明:通过@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<Score> scores;

public void setScores(List<Score> scores) {

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<Score> scores = student.getScores();

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();

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容