JPA和Hibernate的关系
JPA 是 hibernate 的一个抽象(就像JDBC和JDBC驱动的关系):
JPA 是规范:JPA 本质上就是一种 ORM 规范,不是ORM 框架 —— 因为 JPA 并未提供 ORM 实现,它只是制订了一些规范,提供了一些编程的 API 接口,但具体实现则由 ORM 厂商提供实现
Hibernate 是实现:Hibernate 除了作为 ORM 框架之外,它也是一种 JPA 实现
从功能上来说, JPA 是 Hibernate 功能的一个子集
使用JPA持久化对象的步骤
A、创建 persistence.xml, 在这个文件中配置持久化单元,JPA 规范要求在类路径的 META-INF 目录下放置persistence.xml,文件的名称是固定的
1、需要指定跟哪个数据库进行交互;
2、需要指定 JPA 使用哪个持久化的框架以及配置该框架的基本属性
3、在里面配置需要持久化的类
B、创建实体类, 使用 annotation 来描述实体类跟数据库表之间的映射关系.
C、使用 JPA API 完成数据增加、删除、修改和查询操作
1、创建 EntityManagerFactory (对应 Hibernate 中的 SessionFactory);
2、创建 EntityManager (对应 Hibernate 中的Session);
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
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">
<persistence-unit name="jpa" transaction-type="RESOURCE_LOCAL">
<!--
配置用什么ORM框架
1. 实际上配置的是 javax.persistence.spi.PersistenceProvider 接口的实现类
2. 如果JPA项目中只有一个JPA的实现产品,则可以不配置该节点
-->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- 添加持久化类 -->
<class>com.atguigu.jpa.bean.User</class>
<properties>
<!-- 配置数据源信息 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///hibernate1"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="root"/>
<!-- 配置JPA实现产品的属性,即hibernate的属性 -->
<property name="hibernate.format_sql" value="true"/><!-- 是否格式化sql语句 -->
<property name="hibernate.show_sql" value="true"/> <!-- 是否在控制台打印sql语句 -->
<!-- 生成数据表的策略,有就更新,没有就创建 -->
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
创建第一个JPA项目,并且创建持久化类,以及相关操作链接
EntityManager
在 JPA 规范中, EntityManager 是完成持久化操作的核心对象。实体作为普通 Java 对象,只有在调用 EntityManager 将其持久化后才会变成持久化对象。EntityManager 对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。
实体的状态:
新建状态: 新创建的对象,尚未拥有持久性主键。
持久化状态:已经拥有持久性主键并和持久化建立了上下文环境
游离状态:拥有持久化主键,但是没有与持久化建立上下文环境
删除状态: 拥有持久化主键,已经和持久化建立上下文环境,但是从数据库中删除。
EntityManager的方法
find():类似于hibernate的 get()方法
getReference():相当于hibernate的 load()
persist():类似于hibernate的save()使对象由临时状态变为持久化状态。
但是有区别:主键设置了自增,persist()如果再设置主键值,会报错。
remove():类似于hibernate的delete()方法,把对应的记录从数据库删除,该方法只能移除持久化对象,而hibernate还可以移除游离对象。
jpa
User user1 = entityManager.find(User.class, 2);
entityManager.remove(user1);
hibernate
User user = new User();
user.setuId(5);
session.delete(user);
merge()方法:类似于hibernate的save或者update方法、
若传入的是一个临时对象:会创建一个新的对象,把临时对象的值都复制到新的对象中,然后对新的对象进行持久化操作,所以说新的对象有id,而之前的对象没有id。
User user = new User();
user.setUserName("源稚生大家长");
user.setPassword("123456");
user.setEmail("sakura@qq.com");
user.setCreateTime(new Date());
user.setBirth(new Date());
//进行保存
User user1 =entityManager.merge(user);
System.out.println("之前的user:---"+user.getUserId());
System.out.println("新的user1:---"+user1.getUserId());
之前的user:---null
新的user1:---4
若传入的对象是一个游离对象,即传入的对象有id值。若在EntityManager缓存中没有该对象,若在数据库中也没有对应的记录,那么JPA会创建一个新的对象,然后把当前游离对象的属性复制到新创建的对象中,然后对新创建的对象尽心insert即持久化操作。
User user = new User();
user.setUserName("源稚女");
user.setPassword("123456");
user.setEmail("sakura@qq.com");
user.setCreateTime(new Date());
user.setBirth(new Date());
user.setUserId(100);
//进行保存
User user1 =entityManager.merge(user);
System.out.println("之前的user:---"+user.getUserId());
System.out.println("新的user1:---"+user1.getUserId());
之前的user:---null
新的user1:---5
若传入的对象是一个游离对象,即传入的对象有id值。若在EntityManager缓存中没有该对象,在数据库中有对应的记录,那么JPA会查询对应的记录,然后返回对应该记录的一个对象,然后再把游离对象的属性复制到查询出的对象中,最后对查询到的对象进行update操作。
User user = new User();
user.setUserName("乌鸦");
user.setPassword("123456");
user.setEmail("ying@qq.com");
user.setCreateTime(new Date());
user.setBirth(new Date());
user.setUserId(5);
User user1 =entityManager.merge(user);
System.out.println(user == user1);//false
若传入的对象是一个游离对象,即传入的对象有id值。若在EntityManager缓存中有该对象,那么JPA会把游离对象的属性复制到EntityManager缓存的记录(手工查出来)中,然后EntityManager缓存中的对象进行update操作。但是hibernate中不行,因为hibernate中的session不能同时关联两个id相同的对象。
User user = new User();
user.setUserName("诺诺");
user.setPassword("123456");
user.setEmail("ying@qq.com");
user.setCreateTime(new Date());
user.setBirth(new Date());
user.setUserId(4);
User user1 = entityManager.find(User.class, 4);
entityManager.merge(user);
System.out.println(user == user1);
映射单项多对一的映射关系:JPA规范规定多的一段维护外键关系
@ManyToOne来映射多对一的映射关系,可以使用其的fetch属性来修改关联属性的加载策略。
@JoinColumn 来映射外键
//orders表中有一列名为user_id,并且类型为int
@JoinColumn(name="user_id")
@ManyToOne(fetch=FetchType.LAZY)
public User getUser() {
return user;
}
默认情况下不是懒加载,默认情况下使用左外连接的方式来获取n的一端的对象和关联的1的一端的对象
//先保存1的一方,后保存n的一方,减少对数据库的操作
entityManager.persist(user);
entityManager.persist(order1);
entityManager.persist(order2);
不能直接删除1的一方,因为有外键关联
映射单项一对多的映射关系
无论如何都会多出update语句,以为在多的一方不会同时插入外键列
@JoinColumn(name="user_id")
@OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.REMOVE})
public Set<Orders> getOrders() {
return orders;
}
这样配置,最终还是在多的一方的表中多出一列user_id,并且出现外键关联。
双向一对多
就是在user和orders都进行配置,配置如上所示。
但是默认情况下,如果都配置的话,就是双向维护外键关系,
这种情况下:如果先保存多的一方,再保存1的一方,会多出2条update,因为双方都要维护外键,先保存1的一方,会多出一条update语句。
实际应用中,设置多的一方维护外键,1的一方放弃外键的维护
即在1的一方,设置mappedBy,值为多的一方所设置的对应的属性名称。如果设置了mappedBy,那么这一方就不能使用@JoinColumn。
@JoinColumn(name="user_id")
@OneToMany(mappedBy="user")
public Set<Orders> getOrders() {
return orders;
}
@JoinColumn(name="user_id")
@ManyToOne(fetch=FetchType.LAZY)
public User getUser() {
return user;
}
一对一关联映射
//在manager方创建外键,unique设置的是唯一约束
@JoinColumn(name="dept_id",unique=true)
@OneToOne(fetch=FetchType.LAZY)
public Department getDept(){
return dept;
}
@OneToOne(mappedBy="dept")
public Manager getMgr(){
return mgr;
}
多对多的关系映射
//这个是Category表
private Set<Item> items = new HashSet<>();
@ManyToMany(mappedBy="categorys")
public Set<Item> getItems() {
return items;
}
//Item表
private Set<Category> categorys = new HashSet<>();
//@JoinTable映射中间表的名字
//name指向中间表的名字
//joinColumns映射当前表在中间表的外键,
//@JoinColumn中name指定当前类在中间表的外键的名字,
//referencedColumnName指定这个外键列关联哪一列
//inverseJoinColumns另一个表在中间表的外键的名字,即放弃外键维护的那一个表
@JoinTable(name="item_category",
joinColumns={@JoinColumn(name="item_id",referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="cate_id",referencedColumnName="cId")}
)
@ManyToMany
public Set<Category> getCategorys() {
return categorys;
}
JPQL——HelloWorld
JPQL 是一种和 SQL 非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的 SQL 查询,从而屏蔽不同数据库的差异。
JPQL语言的语句可以是 select 语句、update 语句或delete语句,它们都通过 Query 接口封装执行
Query接口封装了执行数据库查询的相关方法。调用 EntityManager 的 createQuery、create NamedQuery 及 createNativeQuery 方法可以获得查询对象,进而可调用 Query 接口的相关方法来执行查询操作。
createQuery:使用jqpl语句进行查询
即是直接使用实体类是名字和属性的名字,而不是使用表的名字,和hibernate的使用相同。
String jpql = "FROM User where userId=?";
Query query = entityManager.createQuery(jpql).setParameter(1, 1);
User user = (User) query.getSingleResult();
createNamedQuery:在实体类上加注解方式,进行查询
@NamedQuery(name="testNameQuery",query="FROM User where userId=?")
@NamedQuery(name="testNameQuery",query="FROM User where userId=?")
@Table(name="tb_user") //为数据库表命名,如果不加,自动为类名
@Entity //将类标记为实体类,对应数据库的表
public class User {
}
Query query = entityManager.createNamedQuery("testNameQuery").setParameter(1, 1);
User user = (User) query.getSingleResult();
System.out.println(user);
createNativeQuery :使用sql语句进行查询
但是查询所有的时候,不能使用*,使用的时候,不会强转成我们需要的类型,如果强转会报类转换异常。
String sql = "SELECT user_name FROM tb_user where userId=?";
Query query = entityManager.createNativeQuery(sql).setParameter(1, 1);
Object b = query.getSingleResult();
System.out.println(b);
使用JPQL还可以实现关联查询,分组查询,子查询,以及使用它的内建函数。
实现修改操作
String jpql = "update User set userName=? where userId=?";
Query query = entityManager.createQuery(jpql).setParameter(1,"李嘉图").setParameter(2, 1);
query.executeUpdate();
实现删除操作
String jpql = "delete User where userId=?";
Query query = entityManager.createQuery(jpql).setParameter(1, 12);
query.executeUpdate();