JPA概述
全称是:Java Persistence API。是SUN公司推出的一套基于ORM的规范。hibernate框架中提供了JPA的实现。
JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
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 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
说白了,JPA规范就是java持久化的api,一套orm接口.
hibernate其实就是jpa规范的一套orm实现
今天我们学习的是纯jpa
使用步骤:
1.导入jar包
hibernate解压目录/lib/jpa/hibernate-entitymanager-5.0.7.Final.jar
2.新建一个核心配置文件
必须放在src/META-INF目录下
名称:persistence.xml
导入约束
<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">
</persistence>
配置持久化单元(jpa的实现类提供商(可选),连接的数据库四个参数,hibernate属性,也可以指定持久化类的位置)
注意:
去掉线程绑定session的配置(只是hibernate的内容)
修改事务类型 transaction-type 使用 resource_local
配置的xml代码如下
<persistence-unit name="mysql" transaction-type="RESOURCE_LOCAL">
<!-- 实现类的提供商(可选) 下面两个人选其一都可以 不写也可以-->
<!-- <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> -->
<!-- <provider>org.hibernate.ejb.HibernatePersistence</provider> -->
<!-- 持久类的位置(可选) -->
<!-- <class>cn.itcast.domain.Customer</class> -->
<!-- 数据库的连接信息,hibenrate的属性 -->
<properties>
<!-- 数据库连接4个参数 -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1:3306/hibernate04_62"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="1234"/>
<!-- hiberante属性 -->
<!-- 是否显示sql -->
<property name="hibernate.show_sql" value="true"/>
<!-- 是否格式化sql -->
<property name="hibernate.format_sql" value="true"/>
<!-- 方言 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<!-- 是否由hibernate来生成表ddl语句及如何生成
常见值:
create:由hibernate创建表,每次执行的时候创建一张新表.若之前存在,删除重建;测试用.
create-drop:由hibernate创建表,每次执行的时候创建一张新表.若之前存在,删除重建;彻底使用完成之后,hibernate删除这表.测试用.
update:由hibernate更新或创建表,若之前没有表,则创建;若现在的表关系发生了该表,还可以自动维护表.常用的
validate:使用表的时候,先校验映射文件和表的映射关系,若对应上了直接使用,若对应不上抛异常
-->
<property name="hibernate.hbm2ddl.auto" value="update"/>
<!-- 配置C3P0连接池 -->
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
<!-- 设置事务的隔离级别 -->
<property name="hibernate.connection.isolation" value="4"/>
</properties>
3.新建持久化类
类上添加注解
@Entity //声明他是一个持久化类
@Table(name=”cst_customer”) //指定对应的表
oid属性上注解
@Id //声明他是oid属性
@GeneratedValue(strategy=GenerationType.IDENTITY)//声明主键生成策略
@Column(name=”列名”) //name可以省略不写 column可以省略不写
普通属性上注解
@Column(name=”列名”) //name可以省略不写 column可以省略不写
domain代码如下
@Entity //声明他是一个持久化类
@Table(name="cst_customer") //指定对应的表
public class Customer {
@Id //声明它是oid
@GeneratedValue(strategy=GenerationType.IDENTITY)//声明主键生成策略
@Column(name="cust_id")
private Long cust_id;
@Column
private String cust_name;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_phone;
private String cust_mobile;
4.抽取EntityManagerFactory
相当于之前的SessionFactory
EntityManager 相当于之前的Session
抽取一个工具类 JPAUtils
提供一个方法 获取EntityManager
//类似于之前的SessionFactory
private static EntityManagerFactory factory ;
static{
factory = Persistence.createEntityManagerFactory("mysql");
}
/**
* 获取一个EntityManger 类似于之前的session
* @return
*/
public static EntityManager getEntityManger(){
return factory.createEntityManager();
}
public static void main(String[] args) {
getEntityManger();
}
5.测试代码
persist(Object obj):保存
更新:
方式1:快照机制
方式2:merge(Object obj) //强制将脱管态对象更新到数据库中.
remove(Object obj)
oid查询
T find(Class 持久化类的字节码对象,id) //立即查询
T getReference(Class 持久化类的字节码对象,id) //延迟加载
//获取Entitymanager
EntityManager em = JPAUtils.getEntityManger();
//获取事务
EntityTransaction tx = em.getTransaction();
//开启事务
tx.begin();
//操作
Customer c1 = new Customer("大毛");
em.persist(c1);
//提交事务
tx.commit();
//释放资源
em.close();
查询api:
获取查询对象 Query
entiteyManager.createQuery(String jpql语句);
jpql几乎和hql一样,下面只写不同的
查询所有:
getResultList()
getSingleResult()
排序查询:
分页查询:
统计查询:
条件查询:
setParameter(int 第几个问号,Object obj);
投影查询:
@Test
//查询所有
public void t1(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//获取query对象
Query query = em.createQuery("from Customer");
//查询所有
List<Customer> list = query.getResultList();
for (Customer c : list) {
System.out.println(c);
}
tx.commit();
em.close();
}
@Test
//查询所有
public void t2(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//获取query对象
Query query = em.createQuery("from Customer order by cust_id desc");
//查询所有
List<Customer> list = query.getResultList();
for (Customer c : list) {
System.out.println(c);
}
tx.commit();
em.close();
}
@Test
//统计查询
public void t3(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//获取query对象
Query query = em.createQuery("select count(*) from Customer");
//查询所有
Object obj = query.getSingleResult();
System.out.println(obj+":"+obj.getClass().getName());
tx.commit();
em.close();
}
@Test
//分页查询
public void t4(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//获取query对象
Query query = em.createQuery("from Customer");
//设置分页参数
query.setFirstResult(0);
query.setMaxResults(3);
//查询所有
List<Customer> list = query.getResultList();
for (Customer c : list) {
System.out.println(c);
}
tx.commit();
em.close();
}
@Test
//条件查询
public void t5(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//获取query对象
Query query = em.createQuery("from Customer where cust_name like ? and cust_source = ?");
//设置参数
query.setParameter(1, "%大毛%");
query.setParameter(2, "网络营销");
//查询所有
List<Customer> list = query.getResultList();
for (Customer c : list) {
System.out.println(c);
}
tx.commit();
em.close();
}
@Test
//投影查询
public void t6(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//获取query对象
Query query = em.createQuery("select new Customer(cust_name,cust_id) from Customer");
//查询所有
/*List<Object[]> list = query.getResultList();
for (Object[] c : list) {
System.out.println(Arrays.toString(c));
}*/
List<Customer> list = query.getResultList();
for (Customer c : list) {
System.out.println(c);
}
tx.commit();
em.close();
}
一对多案例:
持久化类(所有的类中的属性尽量不要出现”_”)
在一的一方配置
在集合上配置
@OneToMany(targetEntity=多的一方的字节码对象,mappedBy=”自己在对方中属性名称”)
在多的一方配置
在对象上配置
@ManyToOne(targetEntity=一的一方的字节码对象)
@JoinColumn(name=”外键字段名称”,referencedColumnName=”指向的主表中的字段名称(一般是主键)”)
我们用客户和联系人来看这一对多的关系
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="cust_id")
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_phone")
private String custPhone;
@Column(name="cust_mobile")
private String custMobile;
//表示当前客户拥有的联系人
/**
* 在一的一方配置
* @OneToMany
* targetEntity:目标实体的字节码对象
* mappedBy:自己在多的一方中属性名称
*/
@OneToMany(targetEntity=Linkman.class,mappedBy="customer")
private Set<Linkman> linkmans = new HashSet<>();
下面是联系人domain
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="lkm_id")
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_qq")
private String lkmQq;
@Column(name="lkm_position")
private String lkmPosition;
@Column(name="lkm_memo")
private String lkmMemo;
//用来表示属于那个客户
//private String lkm_cust_id;
/**
* 在多的一方配置:
* @ManyToOne
* targetEntity:配置一的一方的字节码对象,可以省略(因为在一的一方已经设置过了)
* @JoinColumn:配置外键信息
* name:外键字段名称
* referencedColumnName:指向主表的某个字段的名称(一般都是主键)
*/
@ManyToOne(cascade={CascadeType.PERSIST})
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
private Customer customer;
测试:(默认一的一方已经放弃了外键权)
保存测试代码如下:
@Test
//先不配置级联
public void t1(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//1个客户
Customer c2 = new Customer("浩总");
//1个联系人
Linkman l3 = new Linkman("浩秘");
//双向关联
c2.getLinkmans().add(l3);
l3.setCustomer(c2);
//em.persist(c2);//只保存一的一方行不行?行,只保存一的一方 因为放弃了外键维护权,但是多的一方没有数据
//em.persist(l3);//只保存多的一方行不行?不行,瞬时态对象异常
tx.commit();
em.close();
}
测试级联:
保存一的一方
在一的一方配置
* @OneToMany
* cascade={CascadeType.ALL}:所有的级联操作
* cascade={CascadeType.Persist}:只对persist级联保存起作用
* cascade={CascadeType.Persist,CascadeType.Merge}:只对persist级联保存和merge更新起作用
@Test
/**
* 级联保存:
* 保存一的一方,顺便保存多的一方
* 在一的一方的持久化类中映射关系上配置级联 @OneToMany(..,cascade={CascadeType.persist,CascadeType.merge})
*/
public void t2(){
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c2 = new Customer("浩总");
Linkman l3 = new Linkman("浩秘");
//双向关联
c2.getLinkmans().add(l3);
l3.setCustomer(c2);//因为他放弃了维护权,所以这句不能注释掉,所以这句不能注释掉 否则从表的信息存不上
//保存
em.persist(c2);
tx.commit();
em.close();
}
保存多的一方
@ManyToOne
cascade={CascadeType.Persist}:只对persist级联保存起作用
@Test
/**
* 级联保存:
* 保存多的一方,顺便保存一的一方
* 在多的一方的持久化类中映射关系上配置级联 @ManyToOne(..,cascade={CascadeType.persist,CascadeType.merge})
*/
public void t3(){
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c2 = new Customer("浩总");
Linkman l3 = new Linkman("浩秘");
//双向关联
//c2.getLinkmans().add(l3);
l3.setCustomer(c2);
//保存
em.persist(l3);
tx.commit();
em.close();
}
模拟默认删除:
默认删除:不存在
若需要的默认删除的话我们需要先获取一的一方,通过对象导航查询获取多的一方的集合,解除他们之间的关系(设置一的一方为null)
//模拟默认删除
@Test
public void t1(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//查询浩总
Customer c = em.find(Customer.class, 1L);
//对象导航查询
for(Linkman lk : c.getLinkmans()){
//解除关系
lk.setCustomer(null);
}
em.remove(c);
tx.commit();
em.close();
}
级联删除:
级联具有方向性
在注解上配置 cascade={CascadeType.Remove}
@Test
/**
* 级联删除:
* 删除客户的同时 一并删除联系人
* 需要在客户的持久化类中映射关系上配置 cascade=CascadeType.Remove
*/
public void t3(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//查询浩总
Customer c = em.find(Customer.class, 1L);
System.out.println(c.getCustName());
//em.remove(c);
tx.commit();
em.close();
}
多对多案例:
实体:角色和用户
2个用户 3个角色
配置文件:
被动方
@ManyToMany(targetEntity=对方的字节码对象,mappedBy=”自己在对方中属性名称”)
注意:
mappedBy不能和外键配置同时出现
例如:
@ManyToMany(targetEntity=User.class,mappedBy=”roles”)
//表示那些用户选择该角色
/**
* 被动方
*/ //对方的字节码对象 //自己在对方中属性名称
@ManyToMany(targetEntity=User.class,mappedBy="roles")
主动方
@ManyToMany
@JoinTable(name=”中间表表名”,joinColumns={自己在中间表中的外键信息},inverseJoinColumns={对方在中间表中的外键信息})
例如:
@ManyToMany
@JoinTable(name=”sys_user_role”,joinColumns={
@JoinColumn(name=”user_num”,referencedColumnName=”user_id”)
},
inverseJoinColumns={@JoinColumn(name=”role_num”,referencedColumnName=”role_id”)}
)
自己在中间表中的外键信息 - @JoinColumn(name=”user_num”,referencedColumnName=”user_id”)
对方在中间表中的外键信息 - @JoinColumn(name=”role_num”,referencedColumnName=”role_id”)
级联保存和删除 几乎不用
//表示当前用户拥有的角色
/**
* 主动方配置
* @ManyToMany
* targetEntity:可以不写
* 注意:千万不要写mappedBy
* @JoinTable(name="中间表表名",joinColumns={自己在中间表中的外键信息},inverseJoinColumns={对方在中间表中的外键信息})
*/
//@ManyToMany(cascade={CascadeType.ALL})
@ManyToMany
@JoinTable(name="sys_user_role",joinColumns={ //自己在中间表的外键 //自己的主键名字
@JoinColumn(name="user_num",referencedColumnName="user_id")
},
inverseJoinColumns={ //对方在中间表的外键 //对方的主键名字
@JoinColumn(name="role_num",referencedColumnName="role_id")
}
)
private Set<Role> roles = new HashSet<>();
级联保存和删除 几乎不用
默认删除:和xml一样
默认保存
@Test
//没有设置级联
public void t1(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//1个用户
User u1 = new User("小青");
//1个角色
Role r1 = new Role("员工");
u1.getRoles().add(r1);
r1.getUsers().add(u1);
//em.persist(u1);//只保存用户行不行?不行 瞬时对象异常
em.persist(r1);//只保存角色行不行?行,只在role中有一条记录
tx.commit();
em.close();
}
级联保存
@Test
//级联保存
/**
* 保存用户的同时 保存角色
* 在用户的持久化类上 ManyToMany注解上设置 级联操作
*/
public void t2(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//1个用户
User u1 = new User("小青");
//1个角色
Role r1 = new Role("员工");
u1.getRoles().add(r1);
r1.getUsers().add(u1);
em.persist(u1);
tx.commit();
em.close();
}
默认删除
先删除中间表中记录,再删除相应表中的记录
@Test
//默认删除 没有设置级联
public void t(){
EntityManager em = JPAUtils.getEntityManger();
EntityTransaction tx = em.getTransaction();
tx.begin();
//删除用户
User user = em.find(User.class, 1L);
em.remove(user);
tx.commit();
em.close();
}
级联删除基本不用,这里就不讲了