100-JPA
1.ORM
对象关系映射(Object Relational Mapping,简称 ORM)。
在面向对象的软件开发中,通过 ORM,就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联,操作对象就可以直接操作数据库数据,就可以说这套程序实现了 ORM 对象关系映射
简单的说:ORM 就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。
建立两个映射关系:
- 实体类和表的映射关系
- 实体类中属性和表中字段的映射关系
为什么使用 ORM
当实现一个应用程序时(不使用 ORM),我们可能会写特别多数据访问层的代码,从数据库保存数据、修改数据、删除数据,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。ORM 主要实现程序对象到关系数据库数据的映射。
常见的 ORM 框架:Mybatis(ibatis)、Hibernate。
2. hibernate 与 JPA 概述
2.1 hibernate 框架介绍
Hibernate 是一个开放源代码的 ORM 框架,它对 JDBC 进行了非常轻量级的对象封装,它将 POJO 与数据库表建立映射关系,是一个全自动的 ORM 框架,hibernate 可以自动生成 SQL 语句,自动执行,使得程序员可以随心所欲的使用对象编程思维来操纵数据库。
2.2 JPA 概述
JPA 全称是 Java Persistence API,即 Java 持久化 API,是 SUN 公司推出的一套基于 ORM 的规范,内部是由一系列的接口和抽象类构成。
JPA 通过 JDK 5.0 注解描述 对象-关系表
的映射关系,并将运行期的实体对象持久化到数据库中。
2.3 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.4 JPA 与 hibernate 的关系
JPA 规范本质上就是一种 ORM 规范,注意不是 ORM 框架,因为 JPA 并未提供 ORM 实现,它只是制订了一些规范,提供了一些编程的 API 接口,但具体实现则由服务厂商来提供实现。
JPA 和 Hibernate 的关系就像 JDBC 和 JDBC 驱动的关系,JPA 是规范,Hibernate 除了作为 ORM 框架之外,它也是一种 JPA 实现。
JPA 怎么取代 Hibernate 呢?JDBC 规范可以驱动底层数据库吗?答案是否定的,也就是说,如果使用 JPA 规范进行数据库操作,底层需要 Hibernate 作为其实现类完成数据持久化工作。
3 示例
Hibernate 有两种使用方式,一种是通过配置相应的 xml 文件,然后进行各种初始化工作;另一种便是通过注解的方式。
3.1 开发包介绍
由于 JPA 是 SUN 公司制定的 API 规范,所以我们不需要导入额外的 JPA 相关的 jar 包,只需要导入 JPA 的提供商的 jar 包。我们选择 Hibernate 作为 JPA 的提供商,所以需要导入 Hibernate 的相关 jar 包。
下载网址:http://sourceforge.net/projects/hibernate/files/hibernate-orm/5.0.7.Final/
3.2 配置文件方式 使用 hibernate
在
domain
包下建一个Student
类1
2
3
4
5
6
7
8package com.domain;
public class Student {
private int id;
private String name;
private int age;
// ...get and set
}在数据库中进行建表操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31create table tb_student(
id int(10) primary key auto_increment,
name varchar(20),
age int(3)
);
3. 修改 `hibernate.cfg.xml` 文件
```xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC" -//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mytest</property>
<property name="hibernate.connection.username">root</property>
<!-- 方言设置,不同的数据库有不同的方言信息,以下为mysql的 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!--2.2.显示Hibernate在运行的时候执行的sql语句-->
<property name="hibernate.show_sql">true</property>
<!--2.3.格式化sql-->
<property name="hibernate.format_sql">true</property>
<!--2.4.自动建表-->
<!-- 使用了该配置要是相应的表还没有被创建,那么在运行时hibernate会自动新建一个相应的表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!--mapping resource节点指明了映射文件所在路径-->
<mapping resource="mapping/StudentMapping.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration>在
mapping
目录新建一个StudentMapping.hbm.xml
文件,该文件配置了映射类的信息,具体内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.domain.Student" table="tb_student">
<!-- id字段表示数据库主键生成策略: native 使 Hibernate 可以使用 identity, sequence 或 hilo 算法根据底层数据库的情况来创建主键。-->
<id name="id" column="id" type="int">
<generator class="native"/>
</id>
<!-- type属性会将Java类型转换为sql数据类型,name指实体类中的属性名称,column指在数据库中的列名 -->
<property name="name" column="name" type="java.lang.String" length="20"/>
<property name="age" column="age" type="int"></property>
</class>
</hibernate-mapping>在
com.test
包下新建一个测试类,具体内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30package com.test;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import com.domain.Student;
public class HibernateTest {
public static void main(String[] args) {
Configuration config = new Configuration().configure("hibernate.cfg.xml");
//开启会话工厂
SessionFactory sessionFactory = config.buildSessionFactory();
//开启会话
Session session = sessionFactory.openSession();
//开启事务
Transaction tr = session.beginTransaction();
try {
Student stu = new Student();
stu.setName("隔壁老王");
stu.setAge(17);
//保存数据
session.save(stu);
//记得要提交事务,不然不会保存的呀
tr.commit();
}catch(Exception e) {
e.printStackTrace();
tr.rollback();
}
}
}
3.3 注解方式 使用 hibernate
3.3.1 导入 jar 包
对于 JPA 操作,只需要从 Hibernate 提供的资料中找到我们需要的 jar 导入到工程中即可。maven 工程导入坐标
1 |
|
3.3.2 创建客户的数据库表和客户的实体类
创建客户的数据库表
1
2
3
4
5
6
7
8
9
10CREATE TABLE cst_customer (
cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
cust_name varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
cust_source varchar(32) DEFAULT NULL COMMENT '客户信息来源',
cust_industry varchar(32) DEFAULT NULL COMMENT '客户所属行业',
cust_level varchar(32) DEFAULT NULL COMMENT '客户级别',
cust_address varchar(128) DEFAULT NULL COMMENT '客户联系地址',
cust_phone varchar(64) DEFAULT NULL COMMENT '客户联系电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;创建客户的实体类
1
2
3
4
5
6
7
8
9
10public class Customer implements Serializable {
private Long custId;
private String custName;
private String custSource;
private String custIndustry;
private String custLevel;
private String custAddress;
private String custPhone;
// ... get and set
}
3.3.3 编写实体类和数据库表的映射配置
在实体类上使用 JPA 注解的形式配置映射关系
1 |
|
常用注解的说明
1 |
|
3.3.5 实现保存操作
1 |
|
4. JPA 中的主键生成策略
通过 annotation(注解)来映射 hibernate 实体的,基于 annotation 的 hibernate 主键标识为 @Id
, 其生成规则由 @GeneratedValue
设定的.这里的 @id
和 @GeneratedValue
都是 JPA 的标准用法。
JPA 提供的四种标准用法为 IDENTITY,SEQUENCE,AUTO,TABLE。
具体说明如下:
IDENTITY:主键由数据库自动生成(主要是自动增长型)
1
2
3@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long custId;SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
1
2
3
4@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator="payablemoney_seq")
@SequenceGenerator(name="payablemoney_seq", sequenceName="seq_payment")
private Long custId;1
2
3
4
5
6
7
8
9
10
11
12
13//@SequenceGenerator源码中的定义
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface SequenceGenerator {
//表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中
String name();
//属性表示生成策略用到的数据库序列名称。
String sequenceName() default "";
//表示主键初识值,默认为0
int initialValue() default 0;
//表示每次主键值增加的大小,例如设置1,则表示每次插入新记录后自动加1,默认为50
int allocationSize() default 50;
}AUTO:主键由程序控制
1
2
3@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long custId;TABLE:使用一个特定的数据库表格来保存主键
1
2
3
4
5
6
7
8
9
10@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator="payablemoney_gen")
@TableGenerator(name = "pk_gen",
table="tb_generator",
pkColumnName="gen_name",
valueColumnName="gen_value",
pkColumnValue="PAYABLEMOENY_PK",
allocationSize=1
)
private Long custId;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31//@TableGenerator的定义:
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface TableGenerator {
//表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中
String name();
//表示表生成策略所持久化的表名,例如,这里表使用的是数据库中的“tb_generator”。
String table() default "";
//catalog和schema具体指定表所在的目录名或是数据库名
String catalog() default "";
String schema() default "";
//属性的值表示在持久化表中,该主键生成策略所对应键值的名称。例如在“tb_generator”中将“gen_name”作为主键的键值
String pkColumnName() default "";
//属性的值表示在持久化表中,该主键当前所生成的值,它的值将会随着每次创建累加。例如,在“tb_generator”中将“gen_value”作为主键的值
String valueColumnName() default "";
//属性的值表示在持久化表中,该生成策略所对应的主键。例如在“tb_generator”表中,将“gen_name”的值为“CUSTOMER_PK”。
String pkColumnValue() default "";
//表示主键初识值,默认为0。
int initialValue() default 0;
//表示每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50。
int allocationSize() default 50;
UniqueConstraint[] uniqueConstraints() default {};
}
//这里应用表tb_generator,定义为 :
CREATE TABLE tb_generator (
id NUMBER NOT NULL,
gen_name VARCHAR2(255) NOT NULL,
gen_value NUMBER NOT NULL,
PRIMARY KEY(id)
)
5. JPA 的 API 介绍
5.1 Persistence 对象
Persistence 对象主要作用是用于获取 EntityManagerFactory 对象的。通过调用该类的 createEntityManagerFactory 静态方法,根据配置文件中持久化单元名称创建 EntityManagerFactory。
1 |
|
5.2 EntityManagerFactory
EntityManagerFactory 接口主要用来创建 EntityManager。
1 |
|
EntityManagerFactory内部维护的很多的内容
- 内部维护了数据库信息
- 维护了缓存信息
- 维护了所有的实体管理器对象
- 再创建 EntityManagerFactory 的过程中会根据配置创建数据库表
由于 EntityManagerFactory 是一个线程安全的对象(即多个线程访问同一个 EntityManagerFactory 对象不会有线程安全问题),并且 EntityManagerFactory 的创建极其浪费资源,所以在使用JPA编程时,我们可以对 EntityManagerFactory 的创建进行优化,只需要做到一个工程只存在一个 EntityManagerFactory 即可
5.3 EntityManager
在 JPA 规范中, EntityManager 是完成持久化操作的核心对象。实体类作为普通 java 对象,只有在调用 EntityManager 将其持久化后才会变成持久化对象。EntityManager 对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。
我们可以通过调用 EntityManager 的方法完成获取事务,以及持久化数据库的操作
方法说明:
1 |
|
5.4 EntityTransaction
在 JPA 规范中, EntityTransaction 是完成事务操作的核心对象,对于 EntityTransaction 在我们的 java 代码中承接的功能比较简单
1 |
|
6 抽取 JPAUtil 工具类
1 |
|
7 使用JPA完成增删改查操作
7.1 保存
1 |
|
7.2 修改
1 |
|
7.3 删除
1 |
|
7.4根据id查询
1 |
|
7.5 JPA 中的复杂查询 JPQL
JPQL 全称 Java Persistence Query Language
基于首次在 EJB2.0 中引入的 EJB 查询语言(EJB QL),Java 持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将 SQL 语法和简单查询语义绑定在一起使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的 SQL。
其特征与原生 SQL 语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
7.5.1查询全部
1 |
|
7.5.2 分页查询
1 |
|
7.5.3 条件查询
1 |
|
7.5.4 排序查询
1 |
|
7.5.5统计查询
1 |
|