一、ORM概述
- ORM(Object-Relational Mapping) 表示对象关系映射。在面向对象的软件开发中,通过ORM,就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联,操作对象就可以直接操作数据库数据,就可以说这套程序实现了ORM对象关系映射。简单的说:ORM就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。
- 当实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存数据、修改数据、删除数据,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射。
- 常见的 ORM 框架:Mybatis、Hibernate、Jpa
二、hibernate与JPA
1、hibernate 概述
- Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架。
- hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
2、JPA概述
- JPA的全称是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。
- JPA通过JDK 5.0注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
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 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
4、JPA与hibernate的关系
- JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。
- JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。JPA怎么取代Hibernate呢?JDBC规范可以驱动底层数据库吗?答案是否定的,也就是说,如果使用JPA规范进行数据库操作,底层需要hibernate作为其实现类完成数据持久化工作。
三、案例(一对多)
常用注解说明
@Entity
:指定当前类是持久化实体类,不能省略@Table
作用:指定实体类和表之间的对应关系,可以省略
默认:表名=类名(省略后)
属性:name
:指定数据库表的名称@Id
:指定当前字段是主键。不能省略@GeneratedValue
作用:指定主键的生成方式。。
属性:strategy
:指定主键生成策略。@Column
作用:指定实体类属性和数据库表之间的对应关系
属性:
name
:指定数据库表的列名称。
unique
:是否唯一
nullable
:是否可以为空
inserttable
:是否可以插入
updateable
:是否可以更新
columnDefinition
: 定义建表时创建此列的DDL
secondaryTable
: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字搭建开发环境[重点]@OneToMany
:映射一对多
mapperdBy:指定对方关联属性,表示当前类不管理关联关系,相当于inverse=false
fetch:是否使用懒加载@ManyToOne
:映射多对一
@JoinColumn
:映射外键@ManyToMany
:映射多对多
mappedBy=“对方的关联属性”:相当于inverse=true,便是当前方不管理关联关系
在管理关联关系的一方配置关系表@JoinTable
@JoinTable
name
:关系表名
joinColumns
:当前表在中间表的外键
inverseJoinColumns
:对方表在中间表的外键
第一步:导入 jar 包 hibernate-entitymanager
第二步:编写实体类和数据库表的映射配置(在实体类上使用JPA注解的形式配置映射关系)Customer.java、Order.java
package cn.lemon.domain;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity/*指定当前类是持久化实体类(不能省略)*/
@Table(name = "customer_")/*指定实体类和数据库表的对应关系(可以省略),省略后:表名=类名*/
public class Customer {
@Id/*指定当前字段是主键字段*/
@GeneratedValue(strategy = GenerationType.IDENTITY)/*指定主键的生成形式,GenerationType:主键生成策略*/
@Column(name = "ID")/*指定实体类和数据库表的对应关系*/
private Integer id;
@Column(name = "NAME")
private String name;
/*客户的多个订单*/
@OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)/*一对多映射,mapperBy:指定对方关联属性,fetch:是否使用延迟加载*/
private Set<Order> orders = new HashSet<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}
package cn.lemon.domain;
import javax.persistence.*;
@Entity/*指定当前类是持久化实体类(不能省略)*/
@Table(name = "order_")/*指定实体类和数据库表的对应关系(可以省略),省略后:表名=类名*/
public class Order {
@Id/*指定当前字段是主键字段*/
@GeneratedValue(strategy = GenerationType.IDENTITY)/*指定主键的生成形式,GenerationType:主键生成策略*/
@Column(name = "ID")/*指定实体类和数据库表的对应关系*/
private Integer id;
@Column(name = "NAME")
private String name;
/*关联属性:订单所属的客户*/
@ManyToOne(fetch = FetchType.LAZY)/*多对一映射,fetch = FetchType.LAZY 默认使用延迟加载(可以省略)*/
@JoinColumn(name = "CUST_ID")/*映射外键*/
private Customer customer;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
第三步:配置JPA的核心配置文件(路径:resource/META-INF/persistence.xml)
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--JPA 核心配置文件-->
<!--需要配置persistence-unit节点
持久化单元:
name:持久化单元名称
transaction-type:事务管理的方式
JTA:分布式事务管理
RESOURCE_LOCAL:本地事务管理
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--可选配置:配置jpa实现方的配置信息-->
<properties>
<!-- 数据库信息
用户名,javax.persistence.jdbc.user
密码, javax.persistence.jdbc.password
驱动, javax.persistence.jdbc.driver
数据库地址 javax.persistence.jdbc.url
-->
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="lemon"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///db_javapersistenceapi"/>
<!--配置jpa实现方(hibernate)的配置信息
显示sql : false|true
自动创建数据库表 : hibernate.hbm2ddl.auto
create : 程序运行时创建数据库表(如果有表,先删除表再创建)
update :程序运行时创建表(如果有表,不会创建表)
none :不会创建表
-->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL55Dialect" />
</properties>
</persistence-unit>
</persistence>
第四步: 工具类 JpaUtils.java(用于读取JPA核心配置文件产生 SessionFactory)
package cn.lemon.utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaUtils {
/**
* 解决实体管理器工厂的浪费资源和耗时问题
* 通过静态代码块的形式,当程序第一次访问此工具类时,创建一一个公共的实体管理器工厂对象
* 第一次访问getEntityManager方法: 经过静态代码块创建一 个factory对象, 再调用方法创建一 个EntityManager对象
* 第二次方法getEntityManager方法: 直接通过一-个已经创建好的factory对象,创建EntityManager对象
*/
private static EntityManagerFactory entityManagerFactory;
static {
entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");/*加载配置文件,创建 createEntityManagerFactory*/
}
public static EntityManager getEntityManager() {/*获取 EntityManager 对象*/
return entityManagerFactory.createEntityManager();
}
}
第五步: 测试类
package cn.lemon.test;
import cn.lemon.domain.Customer;
import cn.lemon.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
public class OnetoManyTest {
@Test
public void testCreateTable() {
//当sessionFactory产生时产生表结构
JpaUtils.getEntityManager();
}
@Test
public void testAdd() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = new Customer();
c.setName("腾讯");
entityManager.persist(c);
tx.commit();
entityManager.close();
}
@Test
public void testUpdate() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = new Customer();
c.setId(1);
c.setName("百度");
entityManager.merge(c);
tx.commit();
entityManager.close();
}
@Test
public void testDelete() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = entityManager.find(Customer.class, 3);
entityManager.remove(c);
tx.commit();
entityManager.close();
}
@Test
public void testGet() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = entityManager.find(Customer.class, 1);
System.out.println(c.getName());
tx.commit();
entityManager.close();
}
@Test
public void testLoad() {
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer c = entityManager.getReference(Customer.class, 1); //使用懒加载(session.load())
tx.commit();
entityManager.close();
System.out.println(c.getName());
}
}
四、JPA中的主键生成策略
通过annotation(注解)来映射hibernate实体的,基于annotation的hibernate主键标识为@Id
, 其生成规则由@GeneratedValue
设定的。@id和@GeneratedValue都是JPA的标准用法。
1、IDENTITY
:主键由数据库自动生成(主要是自动增长型)
- 用法:
@GeneratedValue(strategy = GenerationType.IDENTITY)
2、SEQUENCE
:根据底层数据库的序列来生成主键,条件是数据库支持序列。
3、AUTO
:主键由程序控制
- 用法:
@GeneratedValue(strategy = GenerationType.AUTO)
4、TABLE
:使用一个特定的数据库表格来保存主键
五、JPA的API介绍
1、Persistence对象
- Persistence对象主要作用是用于获取EntityManagerFactory对象的 。通过调用该类的createEntityManagerFactory静态方法,根据配置文件中持久化单元名称创建EntityManagerFactory。
2、EntityManagerFactory
- EntityManagerFactory 接口主要用来创建 EntityManager 实例
- 由于EntityManagerFactory 是一个线程安全的对象(即多个线程访问同一个EntityManagerFactory 对象不会有线程安全问题),并且EntityManagerFactory 的创建极其浪费资源,所以在使用JPA编程时,我们可以对EntityManagerFactory 的创建进行优化,只需要做到一个工程只存在一个EntityManagerFactory 即可
3、EntityManager
- 在 JPA 规范中, EntityManager是完成持久化操作的核心对象。实体类作为普通 java对象,只有在调用 EntityManager将其持久化后才会变成持久化对象。EntityManager对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。
- 我们可以通过调用EntityManager的方法完成获取事务,以及持久化数据库的操作
方法说明:
- getTransaction : 获取事务对象
- persist : 保存操作
- merge : 更新操作
- remove : 删除操作
- find/getReference : 根据id查询
4、EntityTransaction
- 在 JPA 规范中, EntityTransaction是完成事务操作的核心对象,对于EntityTransaction在我们的java代码中承接的功能比较简单
- begin:开启事务
- commit:提交事务
- rollback:回滚事务
六、JPQL(Java Persistence Query Language) 查询
package cn.lemon.test;
import cn.lemon.domain.Customer;
import cn.lemon.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;
public class JpqlTest {
@Test
/**
* jpql查询,具体查询案例和应用,参考hibernate的hql查询
*/
public void testFindAll() {
EntityManager entityManager = JpaUtils.getEntityManager();
Query query = entityManager.createQuery("from Customer");
List<Customer> list = query.getResultList();
for (Customer customer : list) {
System.out.println(customer.getName());
}
}
@Test
public void testFindByCondition() {
EntityManager entityManager = JpaUtils.getEntityManager();
Query query = entityManager.createQuery("from Customer where name like ?0"); //jpql java persistense query language
List<Customer> list = query.setParameter(0, "%a%").getResultList();
for (Customer customer : list) {
System.out.println(customer.getName());
}
}
@Test
public void testFindByPage() {
EntityManager entityManager = JpaUtils.getEntityManager();
Query query = entityManager.createQuery("from Customer"); //jpql java persistense query language
List<Customer> list = query.setFirstResult(5).setMaxResults(5).getResultList();
for (Customer customer : list) {
System.out.println(customer.getName());
}
}
/**
* jpa的criteria查询,具体参考hibernate的qbc查询
*/
@Test
public void testFindByCriteria() {
EntityManager entityManager = JpaUtils.getEntityManager();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Customer> criteria = builder.createQuery(Customer.class);
Root<Customer> root = criteria.from(Customer.class);
criteria.select(root);
//条件
criteria.where(builder.like(root.get("name").as(String.class), "%a%"));
List<Customer> list = entityManager.createQuery(criteria).getResultList();
for (Customer customer : list) {
System.out.println(customer.getName());
}
}
}