1. JPA相关概念
1.1 JPA概述
全称是:Java Persistence API。是SUN公司推出的一套基于ORM的规范。Hibernate框架中提供了JPA的实现。
JPA通过JDK 5.0注解或XML描述对象—关系表的映射关系,并将运行期的实体对象持久化到数据库中。
1.2 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 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
1.3 学习JPA要明确的
- JPA是一套ORM规范,Hibernate实现了JPA规范
- hibernate中有自己的独立ORM操作数据库方式,也有JPA规范实现的操作数据库方式。
2. JPA入门
2.1 需求介绍
本章节我们实现基于JPA注解的对象关系映射,配置实体类和数据库表的对应关系。并且使用JPA规范中的方法实现CRUD操作。
2.2 JPA环境搭建
2.2.1 第一步:拷贝jar包
2.2.2 第二步:创建配置文件
在src下面的META-INF文件夹下面创建一个名称为persistence.xml的文件。
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
version="2.0">
<!-- 配置持久化单元,可以配置多个,但是名称不能重复
name:用于指定持久化单元名称
transaction-type:指定事务的类型。
JTA:Java Transaction API
RESOURCE_LOCAL:指的是本地代码事务。(我们用这个)
-->
<persistence-unit name="myJPAUnit" transaction-type="RESOURCE_LOCAL">
<!-- JPA规范的提供商 可以不写。-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 指定由Jpa注解的实体类位置 可以不写。-->
<class>com.wgy.domain.Customer</class>
<!-- 连接库相关的一些配置 -->
<properties>
<!-- 第一部分:连接数据库的信息 -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/hibernate01"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="root"/>
<!-- 数据库的方言 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<!-- 第二部分:hibernate的可选配置 -->
<!-- 是否显示hibernate生成的SQL语句 -->
<property name="hibernate.show_sql" value="true"/>
<!-- 是否使用格式化输出sql语句到控制台 -->
<property name="hibernate.format_sql" value="false"/>
<!-- 配置hibernate采用何种方式生成DDL语句 -->
<!-- update表示检测实体类的映射配置和数据库的表结构是否一致,如果不一致,更新表结构 -->
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
2.2.3 第三步:编写工具类,用于获取JPA的操作数据库对象
/**
* JPA的工具类
*
* @author wgy
*/
public class JPAUtil {
//它就相当于SessionFactory
private static EntityManagerFactory factory;
static {
//注意:该方法参数必须和persistence.xml中persistence-unit标签name属性取值一致
factory = Persistence.createEntityManagerFactory("myJPAUnit");
}
/**
* 获取EntityManager对象
*
* @return
*/
public static EntityManager createEntityManager() {
return em.createEntityManager();
}
}
2.2.4 第四步:编写实体类并使用注解配置
/**
* 客户实体类
* 使用的注解都是JPA规范,所以导包,都需要导入javax.persistence包下的
*
* @author wgy
*/
@Entity//表明该类是一个实体类
@Table(name = "cst_customer")//建立当前类和数据库表的对应关系
public class Customer implements Serializable {
@Id//表明当前字段是主键
@Column(name = "cust_id")//表明对应数据库的主键字段是cust_id
@GeneratedValue(strategy = GenerationType.IDENTITY)//指定主键生成策略。
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;
...
}
2.3 JPA的CRUD操作
2.3.1 保存
/**
* 保存
*/
@Test
public void test1() {
//创建客户对象
Customer c = new Customer();
c.setCustName("JPA Customer");
//1.获取EntityManager对象
EntityManager em = JPAUtil.createEntityManager();
//2.获取事务对象,并开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.执行保存操作
em.persist(c);
//4.提交事务
tx.commit();
//5.关闭资源
em.close();
}
2.3.2 快照更新
/**
* 更新操作
*/
@Test
public void test3() {
//1.获取EntityManager对象
EntityManager em = JPAUtil.createEntityManager();
//2.获取事务对象,并开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.执行更新操作(需要把更新的对象先查询出来)
Customer c = em.find(Customer.class, 1L);
//修改客户的地址为:顺义区
c.setCustAddress("顺义区");
//4.提交事务
tx.commit();
//5.关闭资源
em.close();
}
2.3.3 merge更新
/**
* 更新的另一种操作方式
* merge是合并 (两个实体合并)
*/
@Test
public void test4() {
//1.获取EntityManager对象
EntityManager em = JPAUtil.createEntityManager();
//2.获取事务对象,并开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.执行更新操作(需要把更新的对象先查询出来)
Customer c = em.find(Customer.class, 1L);
//修改客户的地址为:顺义区
c.setCustAddress("北京市顺义区");
em.merge(c);
//4.提交事务
tx.commit();
//5.关闭资源
em.close();
}
2.3.4 删除
/**
* 删除操作
*/
@Test
public void test5() {
//1.获取EntityManager对象
EntityManager em = JPAUtil.createEntityManager();
//2.获取事务对象,并开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.执行更新操作(需要把更新的对象先查询出来)
Customer c = em.find(Customer.class, 1L);
//删除操作
em.remove(c);
//4.提交事务
tx.commit();
//5.关闭资源
em.close();
}
2.3.5 查询一个
/**
* 查询一个实体 立即加载
*/
@Test
public void test2() {
//1.获取EntityManager对象
EntityManager em = JPAUtil.createEntityManager();
//2.获取事务对象,并开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.执行更新操作(需要把更新的对象先查询出来)
Customer c = em.find(Customer.class, 1L);
System.out.println(c);
//4.提交事务
tx.commit();
//5.关闭资源
em.close();
}
/**
* 查询一个实体 延迟加载
*/
@Test
public void test2_1() {
//1.获取EntityManager对象
EntityManager em = JPAUtil.createEntityManager();
//2.获取事务对象,并开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.执行更新操作(需要把更新的对象先查询出来)
Customer c = em.getReference(Customer.class, 1L);
System.out.println(c);
//4.提交事务
tx.commit();
//5.关闭资源
em.close();
}
2.3.6 查询所有
/**
* 查询所有
*
* 涉及的对象是:
* JPA的Query
* 如何获取该对象:
* EntityManager的createQuery(String jpql)
* 参数含义:
* JPQL:Java Persistence Query Language
* 他的写法和HQL很相似。也是把表名换成类名,把字段名换成属性名称
* 它在写查询所有时,不能直接用 from 实体类
* 需要使用select关键字
* select c from Customer c
*/
@Test
public void test6() {
//1.获取EntityManager对象
EntityManager em = JPAUtil.createEntityManager();
//2.获取事务对象,并开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.获取JPA的查询对象Query
Query query = em.createQuery("select c from Customer c where custName like ? and custLevel = ? ");
//给占位符赋值
query.setParameter(1, "%集%");
query.setParameter(2, "23");
//执行方法获取结果集
//getSingleResult():查询结果是一个对象
List list = query.getResultList();
for (Object o : list) {
System.out.println(o);
}
//4.提交事务
tx.commit();
//5.关闭资源
em.close();
}
3. JPA的多表操作
3.1 一对多关系配置及操作
3.1.1 配置
/**
* 客户实体类
*
* @author wgy
*/
@Entity
@Table(name = "cst_customer")
public class Customer implements Serializable {
@Id
@Column(name = "cust_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
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;
/**
* 一对多关系映射:一个客户可以有多个联系人
* mappedBy 取消维护
* cascade 级联保存
* fetch 查询加载时机
*/
@OneToMany(targetEntity = LinkMan.class,mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
...
}
/**
* 联系人的实体类
*
* @author wgy
*/
@Entity
@Table(name = "cst_linkman")
public class LinkMan implements Serializable {
@Id
@Column(name = "lkm_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long lkmId;
@Column(name = "lkm_name")
private String lkmName;
@Column(name = "lkm_gender")
private String lkmGender;
@Column(name = "lkm_phone")
private String lkmPhone;
@Column(name = "lkm_mobile")
private String lkmMobile;
@Column(name = "lkm_email")
private String lkmEmail;
@Column(name = "lkm_position")
private String lkmPosition;
@Column(name = "lkm_memo")
private String lkmMemo;
/**
* 一对多关系映射,多的一方。
* 从表实体包含主表实体的对象引用
*/
@ManyToOne(targetEntity = Customer.class,fetch = FetchType.LAZY)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
...
}
3.1.2 操作
3.1.2.1 保存
/**
* 保存操作
* 创建一个客户和一个联系人
* 建立客户和联系人的双向关联关系
* 先保存客户,再保存联系人
*/
@Test
public void test1() {
Customer c = new Customer();
LinkMan l = new LinkMan();
c.setCustName("JPA One To Many Customer");
l.setLkmName("JPA One To Many LinkMan");
c.getLinkmans().add(l);
l.setCustomer(c);
EntityManager em = JPAUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(c);
em.persist(l);
tx.commit();
em.close();
}
3.1.2.2 更新
/**
* 更新操作
* 创建一个联系人
* 查询id为5的客户
* 为5这个客服分配该联系人
* 更新客户
*/
@Test
public void test2() {
LinkMan l = new LinkMan();
l.setLkmName("JPA One To Many LinkMan 2");
EntityManager em = JPAUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c = em.find(Customer.class, 5L);
c.getLinkmans().add(l);
l.setCustomer(c);
tx.commit();
em.close();
}
3.1.2.3 删除
/**
* 删除操作
*/
@Test
public void test3() {
EntityManager em = JPAUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c = em.find(Customer.class, 5L);
em.remove(c);
tx.commit();
em.close();
}
3.1.2.4 查询
/**
* 根据客户查询客户下的联系人
*/
@Test
public void test1() {
EntityManager em = JPAUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//1.查询id为1的客户
Customer c = em.find(Customer.class, 1l);
System.out.println(c);
//查询当前客户下的联系人
Set<LinkMan> linkmans = c.getLinkmans();
System.out.println(linkmans);
tx.commit();
em.close();
}
/**
* 根据联系人,查询联系人的所属客户
*/
@Test
public void test2() {
EntityManager em = JPAUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//1.查询id为1的客户
LinkMan l = em.find(LinkMan.class, 1l);
System.out.println(l);
//查询当前客户下的联系人
Customer c = l.getCustomer();
System.out.println(c);
tx.commit();
em.close();
}
3.2 多对多关系配置及操作
3.2.1 配置
/**
* 角色的实体类
*
* @author wgy
*/
@Entity
@Table(name = "sys_role")
public class SysRole implements Serializable {
@Id
@Column(name = "role_id")
@GenericGenerator(name = "uuid", strategy = "uuid")//声明一个主键生成器 name属性:给生成器起个名字。strategy:指定的就是hibernate中包含的生成策略
@GeneratedValue(generator = "uuid")
private String roleId;
@Column(name = "role_name")
private String roleName;
@Column(name = "role_memo")
private String roleMemo;
//多对多关系映射:一个角色可以赋予多个用户
@ManyToMany(cascade=CascadeType.ALL)
//加入一张表
@JoinTable(name="user_role_ref",
joinColumns = {@JoinColumn(name="role_id",referencedColumnName="role_id")},//写的是当前实体在中间表的外键字段
inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")}//写的是对方实体在中间表的外键字段
)
private Set<SysUser> users = new HashSet<SysUser>(0);
...
}
/**
* 用户的实体类
*
* @author wgy
*/
@Entity
@Table(name = "sys_user")
public class SysUser implements Serializable {
@Id
@Column(name = "user_id")
@GenericGenerator(name = "uuid", strategy = "uuid")
@GeneratedValue(generator = "uuid")
private String userId;
@Column(name = "user_name")
private String userName;
@Column(name = "user_password")
private String userPassword;
@Column(name = "user_state")
private Integer userState;
//多对多关系映射:一个用户可以具备多个角色
@ManyToMany(mappedBy="users",cascade= CascadeType.ALL)
private Set<SysRole> roles = new HashSet<SysRole>(0);
...
}
3.2.2 操作
3.2.2.1 保存
/**
* 保存操作
* 创建两个用户
* 创建三个角色
* 让1号用户具备1号和2号角色
* 让2号用户具备2号和3号角色
* 保存用户和角色
*/
@Test
public void test1() {
SysUser u1 = new SysUser();
SysUser u2 = new SysUser();
u1.setUserName("JPA Many to Many u1");
u2.setUserName("JPA Many to Many u2");
SysRole r1 = new SysRole();
SysRole r2 = new SysRole();
SysRole r3 = new SysRole();
r1.setRoleName("JPA Many to Many r1");
r2.setRoleName("JPA Many to Many r2");
r3.setRoleName("JPA Many to Many r3");
//建立用户和角色的关联关系
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r2);
u2.getRoles().add(r3);
r1.getUsers().add(u1);
r2.getUsers().add(u1);
r2.getUsers().add(u2);
r3.getUsers().add(u2);
EntityManager em = JPAUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//保存操作
em.persist(u1);
tx.commit();
em.close();
}
3.2.2.2 删除
/**
* 删除操作
* 双向级联删除,不管是hibernate还是Jpa,多对多中都不能配置
*/
@Test
public void test2() {
EntityManager em = JPAUtil.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//查询用户
SysUser u1 = em.find(SysUser.class, "8a7e83cc5fbf19b6015fbf19bada0000");
em.remove(u1);
tx.commit();
em.close();
}
4. JPA的其他说明
4.1 JPA中使用C3P0连接池
4.1.1 第一步:拷贝C3P0所必须的3个jar包
4.1.2 第二步:在persistence.xml配置文件中配置
<!-- 配置使用C3P0数据源 -->
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
4.1.3 验证是否配置成功
/**
* 验证c3p0连接池是否配置成功
*/
@Test
public void test1() {
//1.获取jpa中的操作对象
EntityManager em = JPAUtil.createEntityManager();
//2.
Session session = em.unwrap(Session.class);
//3.执行session的doWork方法
session.doWork(new Work() {
@Override
public void execute(Connection conn) throws SQLException {
System.out.println(conn.getClass().getName());
}
});
}
@Test
public void test2() {
//1.获取jpa中的操作对象
EntityManager em1 = JPAUtil.createEntityManager();
EntityManager em2 = JPAUtil.createEntityManager();
//false
System.out.println(em1 == em2);
}
4.2 JPA与Hibernate中操作数据的方法对照
5. Hibernate中使用JPA注解映射配置
5.1 编写主配置文件(hibernate.cfg.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 在类的根路径下创建名称为hibernate.cfg.xml的配置文件导入约束:dtd约束 -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- 配置SessionFactory
SessionFactory的作用就是用于创建Session对象的。
Session对象就是hibernate中操作数据库的核心对象。
此处的配置不要求背,但是要求记住创建SessionFactory必须的三部分信息
第一部分:
连接数据库的信息
第二部分:
hibernate的可选配置
第三部分:
映射文件的位置
-->
<session-factory>
<!-- 第一部分:连接数据库的信息 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 数据库的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 第二部分:hibernate的可选配置 -->
<!-- 是否显示hibernate生成的SQL语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 是否使用格式化输出sql语句到控制台 -->
<property name="hibernate.format_sql">true</property>
<!-- update表示检测实体类的映射配置和数据库的表结构是否一致,如果不一致,更新表结构 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 设置hibernate的连接池提供商 -->
<property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider
</property>
<!-- 把session和线程绑定,从而实现一个线程只有一个Session -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 第三部分:映射配置文件的位置 -->
<mapping class="com.wgy.domain.Customer"/>
</session-factory>
</hibernate-configuration>
5.2 编写实体类并使用JPA注解配置
/**
* 客户实体类
*
* @author wgy
*/
@Entity
@Table(name = "cst_customer")
public class Customer implements Serializable {
@Id
@Column(name="cust_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
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;
...
}
5.2 操作
5.2.1 保存
/**
* 保存
*/
@Test
public void test1() {
Customer c = new Customer();
c.setCustName("hibernate jpa customer");
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
s.save(c);
tx.commit();
}
5.2.2 查询
/**
* 查询一个
*/
@Test
public void test2() {
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
Customer c = s.get(Customer.class, 6L);
System.out.println(c);
tx.commit();
}
5.2.3 修改
/**
* 修改
*/
@Test
public void test3() {
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
Customer c = s.get(Customer.class, 6L);
c.setCustAddress("顺义区");
tx.commit();
}
5.2.4 删除
/**
* 删除操作
*/
@Test
public void test4() {
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
Customer c = s.get(Customer.class, 6L);
s.delete(c);
tx.commit();
}