1 、ORM 概述[ 了解]
ORM(Object-Relational Mapping)表示对象关系映射。在面向对象的软件开发中,通过 ORM,就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联,操作对象就可以直接操作数据库数据,就可以说这套程序实现了 ORM 对象关系映射
简单的说:ORM 就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。
1.1 为什么使用 ORM
当实现一个应用程序时(不使用 O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存数据、修改数据、删除数据,而这些代码都是重复的。而使用 ORM 则会大大减少重复性代码。
对象关系映射(Object Relational Mapping,简称 ORM),主要实现程序对象到关系数据库数
据的映射。
1.2 常见 ORM 框架
常见的 orm 框架: Hibernate、OpenJpa、Toplink
2 、hibernate 与 与 JPA 的概述[ 了解]
2.1 hibernate 概述
- Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,它将 POJO 与数据库表建立映射关系,是一个全自动的 orm 框架,hibernate 可以自动生成 SQL 语句,自动执行,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。
2.2 JPA 概述
JPA 的全称是 Java Persistence API, 即 Java 持久化 API,是 SUN 公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。
JPA 通过 JDK 5.0 注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
2.3 JPA 的优势
1. 标准化
- JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样
的架构,提供相同的访问 API,这保证了基于 JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。
2. 容器级特性的支持
- JPA 框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局
限,在企业应用发挥更大的作用。
3. 简单方便
- JPA 的主要目标之一就是提供更加简单的编程模型:在 JPA 框架下创建实体和创建 Java 类一
样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity 进行注释,JPA 的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA
基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成
4. 查询能力
- JPA 的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是 Hibernate HQL 的等价物。JPA 定义了独特的 JPQL(Java Persistence Query Language),JPQL 是 EJB QL 的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL才能够提供的高级查询特性,甚至还能够支持子查询。
5. 高级特性
- 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、JPA 开发配置
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 搭建开发环境[ 重点]
3.2.1 maven 工程导入坐标
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- log日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
3.2.2 配置文件 persistence.xml
- 目录:
persistence.xml
必须按照目录放在 META-INF 根目录下,且文件夹名必须为 META-INF
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!-- persistence-unit: 配置持久化单元
属性:
name: 持久化单元的名称
transaction-type: 指定的是事务的类型
JTA: 分布式事务
RESOURCE_LOCAL : 本地事务
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!-- 指定jpa的实现方 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 配置实现方的属性 -->
<properties>
<!-- 数据库连接四要素 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://dbnode01:3306/jpa?useUnicode=true&characterEncoding=UTF-8"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="passw0rd"/>
<!-- 可选属性:在控制台打印sql语句 -->
<property name="hibernate.show_sql" value="true"/>
<!-- 让hibernate来维护表:hibernate.hbm2ddl.auto
create: 每一次 都会 先删除表,再创建表
update: 有表就维护表,没有表就创建表
none: 什么也不干
-->
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
3.2.3 创建客户的数据库表和客户的实体类
- 创建客户的数据库表
/*创建客户表*/
CREATE 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;
- 创建客户的实体类
import javax.persistence.*;
/**
* 客户的实体类
* 导入的jpa的注解:都来自javax.persistence包下的注解
* 用到的注解:
* 1、@Entity :标注当前类是实体类
* 2、@Table: 指定实体类与那张表的映射,name属性指定表名
* 3、@Id : 指定主键属性
* 4、@GeneratedValue: strategy 指定主键的策略: IDENTITY,SEQUENCE,TABLE,AUTO
* 5、@Column: name属性就是指定的当前属性与表中的那个字段进行映射
*
* 主键生成策略: IDENTITY,SEQUENCE,TABLE,AUTO
* 1、IDENTITY: 适用于有自增长策略的数据库: 例如 MySql数据库 ,AUTO_INCREMENT 【重点】
* 2、SEQUENCE: 适用于有序列机制的数据库: 例如Oracle数据库 【重点】
* 3、TABLE: 采用一张数据库表来维护主键,一般不用
* 4、AUTO: 官方:根据数据库选择最优的主键策略 , 不管怎个搞都是采用Table,一般不用
*
*/
@Entity
@Table(name = "cst_customer")
public class Customer {
@Id
@Column(name="cust_id")
@GeneratedValue(strategy = GenerationType.IDENTITY) //mysql:主键数据库自增长策略
/*
Oracle的序列机制
@GeneratedValue(
//策略
strategy = GenerationType.SEQUENCE,
//采用那个生成器
generator = "abc")
@SequenceGenerator(
//生成器的名称,给策略引用
name="abc",
//序列的名称:具体数据库中存在
sequenceName = "seq_customer_id",
//初始值
initialValue = 1,
//步长
allocationSize = 1
)*/
private Long custId;
@Column(name="cust_name")
private String custName;
@Column(name="cust_source")
private String custSource;
@Column(name="cust_industry")
private String custIndustry;
@Column(name="cust_level")
private String custLevel;
@Column(name="cust_address")
private String custAddress;
@Column(name="cust_phone")
private String custPhone;
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custLevel='" + custLevel + '\'' +
", custAddress='" + custAddress + '\'' +
", custPhone='" + custPhone + '\'' +
'}';
}
}
4、JPA 入门及 API 介绍
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class Test01_JPA {
/**
* 需求: 保存一个客户实体到客户表中
* 入门代码的步骤:
* 1、根据配置文件创建EntityManagerFactory
* 2、获取EntityManager
* 3、获取事务对象EntityTransaction
* 4、开启事务
* 5、进行CRUD操作
* 6、提交事务|回滚事务
* 7、释放资源
*
* JPA的api的介绍:
* 1、Persistence: 重要程度:一般
* 作用:用于创建EntityManagerFactory的,根据持久化单元来创建,在jpa的核心配置文件中配置好的
* 常用的方法: createEntityManagerFactor("持久化单元名称");
*
* 2、EntityManagerFactory: 重要程度:比较重要
* 作用: 用于创建实体类管理器:EntityManager
* 常用的方法:createEntityManger()
* 细节:
* 1)、这个对象是个重量级的对象,它维护了很多信息,包括核心配置文件、全字段插入、全字段更新、根据id删除的预编译sql
* 2)、它是个线程安全的对象,多线程访问的时候,不会出现并发问题
* 3)、一个web项目应该只有一个这个对象
*
* 3、EntityManager: 重要程度:非常重要
* 作用: 获取事务对象,与数据库进行交互的,所有的增删改查都是他来完成
* 常用的方法:
* getTransaction();//获取事务对象
* persist();//保存一个对象
* merge();//更新
* remove();//删除
* find()| getReference() ;//查询一个对象
* createQuery();//进行一些复杂查询,返回的是Query对象
* 细节:
* 由于工厂已经维护了较多的信息,所以这个对象维护的信息就少了,每次操作都应该是一个新的对象
*
* 4、EntityTransaction: 重要程度:会用就行
* 作用: 控制事务
* 常用方法:
* begin();//开启事务
* commit();//提交事务
* rollback();//回滚事务
*
*
*/
@Test
public void test1(){
Customer c = new Customer();
c.setCustName("纯情小鸭鸭电脑公司");
c.setCustLevel("倔强青铜");
c.setCustAddress("火星");
//1、根据配置文件创建EntityManagerFactory
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//2、获取EntityManager
EntityManager em = factory.createEntityManager();
//3、获取事务对象EntityTransaction
EntityTransaction tx = em.getTransaction();
//4、开启事务
tx.begin();
//5、进行CRUD操作: EntityManager 与数据库交互
em.persist(c);
//6、提交事务|回滚事务
tx.commit();
//7、释放资源
em.close();
factory.close();
}
}
5、测试增删改查
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
/**
* 测试增删改查
*/
public class Test03_jpa_CRUD {
/**
* 新增:调用persist方法:创建一个对象即可
*/
@Test
public void testAdd(){
Customer c = new Customer();
c.setCustName("纯情小鸭鸭电脑公司");
c.setCustLevel("倔强青铜");
c.setCustAddress("9巷");
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//5、进行CRUD操作: EntityManager 与数据库交互
em.persist(c);
//==============================================
tx.commit();
em.close();
}
/**
* 查询一个对象:
* find(): 查询一个对象: 立即加载:不管用不用都马上发送sql语句查询
* 第一个参数:实体类的字节码对象
* 第二个参数:主键的值
* getReference(): 查询一个对象: 延迟加载【懒加载】:用到的时候才去发送sql语句查询
* 第一个参数:实体类的字节码对象
* 第二个参数:主键的值
*
* 延迟加载:为了提升性能
* 当单个实体对象的数据很庞大的时候用延迟加载;
* 当单个实体对象数据没那么大就立即加载
*/
@Test
public void testFindOne(){
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//进行CRUD操作: EntityManager 与数据库交互
Customer c = em.find(Customer.class, 2L);
//Customer c = em.getReference(Customer.class, 2L);
System.out.println(c);
//==============================================
tx.commit();
em.close();
}
/**
* 更新: merge方法
*/
@Test
public void testUpdate(){
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//进行CRUD操作: EntityManager 与数据库交互
//第一种方式: 有可能会清空原来的数据
/*Customer c = new Customer();
c.setCustId(2L);
c.setCustName("纯情小野鸡游艇公司");*/
//第二种:先根据id查询对象,在更新【推荐】
Customer c = em.find(Customer.class,2L);
c.setCustName("纯情小野鸡游艇公司");
em.merge(c);
//==============================================
tx.commit();
em.close();
}
/**
* 删除
*/
@Test
public void testDelete(){
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//进行CRUD操作: EntityManager 与数据库交互
//直接删除一个new出来的对象会报错
/* Customer c = new Customer();
c.setCustId(2L);*/
//一般都是先查询再删除
Customer c = em.find(Customer.class, 2L);
em.remove(c);
//==============================================
tx.commit();
em.close();
}
}
6、JPA 中的复杂查询:JPQL
JPQL 全称 Java Persistence Query Language
基于首次在 EJB2.0 中引入的 EJB 查询语言(EJB QL),Java 持久化查询语言(JPQL)是一种可移
植的查询语言,旨在以面向对象表达式语言的表达式,将 SQL 语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的 SQL。- 其特征与原生 SQL 语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import java.util.List;
/**
* 复杂查询: JQPL,它是jpa中的查询语言,把sql中的表名换成类的名称,把sql中的字段名换成类的属性名
*
* sql: select * from cst_customer where cust_name like ?
* jpql_1: from Customer where custName like ? 【推荐】
* jpql_2: from com.itheima.domain.Customer where custName like ?
*
* 需要EntityManager创建一个查询对象:
* 1、获取Query对象:EntityManager.createQuery(jpql);
* 2、通过Query对象对占位符赋值
* 3、通过Query对象设置分页、对象的获取
*/
public class Test04_jpa_JPQL {
/**
* 查询所有
* sql: select * from cst_customer
* jpql: from Customer
*/
@Test
public void test1(){
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//5、进行CRUD操作: EntityManager 与数据库交互
String jpql = "from Customer";
//获取查询对象
Query query = em.createQuery(jpql);
//获取查询数据
List<Customer> list = query.getResultList();
for (Customer customer : list) {
System.out.println(customer);
}
//==============================================
tx.commit();
em.close();
}
/**
* 条件查询:
* sql: select * from cst_customer where cust_name like ? and cust_level = ?
* jpql: from Customer where custName like ? and custLevel = ?
*/
@Test
public void test2(){
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//5、进行CRUD操作: EntityManager 与数据库交互
String jpql = "from Customer where custName like ? and custLevel = ?";
//获取查询对象
Query query = em.createQuery(jpql);
//对占位符赋值,索引从1开始
query.setParameter(1,"纯情%");
query.setParameter(2,"倔强青铜");
//获取查询数据
List<Customer> list = query.getResultList();
for (Customer customer : list) {
System.out.println(customer);
}
//==============================================
tx.commit();
em.close();
}
/**
* 分页查询
* mysql: limit
* oracle: rownum
*
* JPA的分页:靠两个方法
* 第一个方法:设置开始索引 : setFirstResult(0)
* 第二个方法:设置每页几条数据,页面大小 setMaxResults(2)
*/
@Test
public void test3(){
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//5、进行CRUD操作: EntityManager 与数据库交互
String jpql = "from Customer where custName like ? ";
//获取查询对象
Query query = em.createQuery(jpql);
//对占位符赋值,索引从1开始
query.setParameter(1,"纯情%");
//分页查询:查询第一页:每页两条
/*query.setFirstResult(0);
query.setMaxResults(2);*/
//分页查询:查询第二页:每页两条
query.setFirstResult(2);//(当前页-1)*pageSize
query.setMaxResults(2);
//获取查询数据
List<Customer> list = query.getResultList();
for (Customer customer : list) {
System.out.println(customer);
}
//==============================================
tx.commit();
em.close();
}
/**
* 排序:
* sql: select * from cst_customer order by cust_id asc|desc
* jpql: from Customer order by custId asc|desc
*/
@Test
public void test4(){
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//5、进行CRUD操作: EntityManager 与数据库交互
String jpql = "from Customer order by custId desc";
//获取查询对象
Query query = em.createQuery(jpql);
//获取查询数据
List<Customer> list = query.getResultList();
for (Customer customer : list) {
System.out.println(customer);
}
//==============================================
tx.commit();
em.close();
}
/**
* 统计查询: count 、sum、max、min、avg
*
* sql: select count(*) from cst_customer
* select count(cust_id) from cst_customer
*
* jpql: select count(*) from Customer
* select count(custId) from Customer
*/
@Test
public void test5(){
EntityManager em = JpaUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//==============================================
//5、进行CRUD操作: EntityManager 与数据库交互
//String jpql = "select count(*) from Customer";
String jpql = "select count(custId) from Customer";
//String jpql = "select sum(custId) from Customer";
//获取查询对象
Query query = em.createQuery(jpql);
//如果我们已经确定了结果只会返回一条数据,可以用下面的方法
Object count = query.getSingleResult();
System.out.println(count);
//获取查询数据:返回的是集合
/*List<Object> list = query.getResultList();
for (Object o : list) {
System.out.println(o);
}*/
//==============================================
tx.commit();
em.close();
}
}
工具类
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* jpa的工具类
* 作用: 保证EntityManagerFactory只创建一次
*/
public class JpaUtil {
//实体类管理器工厂: 这个工厂不能关闭
private static EntityManagerFactory factory;
//静态代码块来完成
static{
factory = Persistence.createEntityManagerFactory("myJpa");
}
//提供一个方法获取实体类管理器
public static EntityManager createEntityManager(){
return factory.createEntityManager();
}
}