JPA全称(JavaPersistenceAPI),它是官方提出的Java持久化规范。需要有Provider实现功能。而Hibernate就是JPA Provider中最强的一个。
JPA包括以下3个方面的技术
1)ORM映射元数据。
2)API,用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情。
3)查询语言,通过面向对象而非面向数据库的查询语句查询数据,避免程序的SQL语句耦合。
一、Hibernate简介
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个"全自动"的ORM框架,hibernate可以自动生成SQL,自动执行。且移植性好。它可以将对象中的数据自动存储到数据库中,也可以反过来将数据库中的数据自动提取到对象中。但仅对于一些常用的简单的SQL语句,一些比较复杂的语句还是要自己构建。
ORM映射注解
注解 | 描述 |
---|---|
@Entity | 将POJO标注为持久化类 |
@Table | 映射表名 |
@Id | 映射表的主键,可用于修饰属性或方法 |
@Column | 映射表的字段,修饰属性或方法 |
@GeneratedValue | 指定自动增长列的类型。generator指定生成器的名称,strategyz指定生成器的类型。 |
@SeqenceGenerator | 序列生成器,与@GeneratedValue配合使用。sequenceName数据库列名,allocationSize增量,默认为50,必须与数据库一致。initialValue初始值,默认为0,必须与数据库一致。name序列生成器的名称 |
二、对象的持久化
狭义的理解:“持久化”仅仅指将对象永久保存到数据库中
广义的理解:“持久化”包括和数据库相关的各种操作
~ 保存:将对象永久保存到数据库中。
~ 更新:更新数据库中对象(记录)的状态。
~ 删除:从数据库中删除一个对象。
~ 查询:根据特定的查询条件。将符合查询条件的一个或多个对象从数据库加载到内存中。
~ 加载:根据特定的OID,将一个对象从数据库加载到内存中。
为了在系统中能够找到所需对象,需要为每一个对象分配一个唯一的标识号。在关系数据库中称之为主键,而在对象术语中,则叫对象标识(Object identifier-OID)
三、ORM(Object Relation Mapping对象关系映射)
-
ORM(Object/Relation Mapping);对象/关系映射
- ORM主要解决对象-关系的映射。
面向对象概念 | 面向关系概念 |
---|---|
类 | 表 |
对象 | 表的行(记录) |
属性 | 表的列(字段) |
-
~ ORM的思想:将关系数据库中表中的记录映射成为对象,以对象的形式展现,
- ORM采用元数据来描述对象-关系映射细节,元数据通常采用XML格式,并且存放在专门的对象-关系映射文件中。
程序员可以将数据库的操作转化为对对象的操作。
四、Hibernate JPA API
EntityManagerFactory 实体管理器工厂
EntityManager 实体管理器
EntityTransaction 事务管理接口
EntityManager常用方法:
方法 | 作用 |
---|---|
persist(Object entity) | 保存 |
merge(T entity) | 更新 |
remove(Object entity) | 删除 |
find(Class< T > entityClass,Object primaryKey) | 根据id查询 |
createQuery(String sqlString,Class< T > resultClass) | 查询 |
createNamedQuery(String name,Class< T > resultClass) | 命名查询,配置@NamedQueries和@NamedQuery |
五、构建一个简单的Hibernate项目
工具使用IntelliJ IDEA
一、新建一个Maven项目
二、导入Hibernate-core的依赖(自行配置),数据库驱动我选择的是Oracle的ojdbc6。
三、在IDEA中连接数据库
四、添加JPA模块及在项目resources\META-INF目录下加入persistence.xml配置文件,provider选择Hibernate。
五、编辑persistence.xml配置。
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="wsl_JPA" transaction-type="RESOURCE_LOCAL">
<provider>
org.hibernate.jpa.HibernatePersistenceProvider
</provider>
<class>com.example.pojo.SalGrade</class>
<properties>
<property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver"/>
<property name="hibernate.connection.username" value="hr"/>
<property name="hibernate.connection.password" value="123456"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
六、建立实体类
第一步打开左侧工具栏中的Persistence窗口,右键单机项目工件,选择生成持久性映射,通过数据库框架。
下面是我生成的实体类,ID需要自行配置。
package com.example.pojo;
import javax.persistence.*;
@Entity(name = "")
@Table(name = "SAL_GRADE", schema = "HR", catalog = "")
public class SalGrade {
private Long grade;
private Long losal;
private Long hisal;
@Id
@Column(name = "GRADE", nullable = false, precision = 0)
@SequenceGenerator(name = "sal_grade_sqq",sequenceName = "sal_grade_sq",initialValue = 1,allocationSize = 1)
@GeneratedValue(generator = "sal_grade_sqq",strategy = GenerationType.SEQUENCE)
public Long getGrade() {
return grade;
}
public void setGrade(Long grade) {
this.grade = grade;
}
@Basic
@Column(name = "LOSAL", nullable = true, precision = 0)
public Long getLosal() {
return losal;
}
public void setLosal(Long losal) {
this.losal = losal;
}
@Basic
@Column(name = "HISAL", nullable = true, precision = 0)
public Long getHisal() {
return hisal;
}
public void setHisal(Long hisal) {
this.hisal = hisal;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SalGrade salGrade = (SalGrade) o;
if (grade != null ? !grade.equals(salGrade.grade) : salGrade.grade != null) return false;
if (losal != null ? !losal.equals(salGrade.losal) : salGrade.losal != null) return false;
if (hisal != null ? !hisal.equals(salGrade.hisal) : salGrade.hisal != null) return false;
return true;
}
@Override
public int hashCode() {
int result = grade != null ? grade.hashCode() : 0;
result = 31 * result + (losal != null ? losal.hashCode() : 0);
result = 31 * result + (hisal != null ? hisal.hashCode() : 0);
return result;
}
}
到这一个简单Hibernate项目就搭建完成了。
七、测试
import com.example.pojo.Dept;
import com.example.pojo.Emp;
import com.example.pojo.SalGrade;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;
public class MyTest {
public static EntityManagerFactory factory;
public EntityManager em;
public EntityTransaction tx;
@BeforeClass
public static void init(){
factory = Persistence.createEntityManagerFactory("wsl_JPA");
}
@Before
public void before(){
em = factory.createEntityManager();
tx = em.getTransaction();
}
@After
public void after(){
em.close();
factory.close();
}
//新增数据
@Test
public void t() {
try{
tx.begin();
SalGrade salGrade = new SalGrade();
salGrade.setLosal(100L);
salGrade.setHisal(1000L);
em.persist(salGrade);
tx.commit();
}catch (Exception e){
tx.rollback();
e.printStackTrace();
}
}
//修改数据
@Test
public void t1() {
try{
tx.begin();
SalGrade salGrade = new SalGrade();
salGrade.setGrade(1L);
salGrade.setLosal(100L);
salGrade.setHisal(10000L);
em.merge(salGrade);
tx.commit();
}catch (Exception e){
tx.rollback();
e.printStackTrace();
}
}
//删除数据
@Test
public void t2() {
try{
tx.begin();
SalGrade salGrade = em.find(SalGrade.class, 1L);
if(salGrade != null){
em.remove(salGrade);
tx.commit();
}
}catch (Exception e){
tx.rollback();
e.printStackTrace();
}
}
//查询指定数据
@Test
public void t3() {
SalGrade salGrade = em.find(SalGrade.class, 1L);
System.out.println(salGrade);
}
//查询所有的数据
@Test
public void t4() {
//from查询的是表名
String sql = "select e from SalGrade e";
List<SalGrade> list = em.createQuery(sql, SalGrade.class).getResultList();
list.forEach(e-> System.out.println(e));
}
@Test
public void t5() {
EntityManagerFactory factory =
Persistence.createEntityManagerFactory("wsl_JPA");
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
try{
tx.begin();//开启事务
SalGrade salGrade = em.find(SalGrade.class, 1L);
salGrade.setLosal(200L);
em.merge(salGrade);
tx.commit();//提交事务
} catch(Exception e){
e.printStackTrace();
tx.rollback();//回滚事务
} finally{
em.close();
factory.close();
}
}
}
六、一对多与多对一关联
一对多
一对多关系使用@OneToMany标注,一般用于标注在集合属性上,如部门类下的Emp集合。
@OneToMany的mappedBy属性值为此关系的另一端的关联属性名。fetch为获取数据的方式,值为枚举类FetchType下的属性。FetchType.EAGER只产生一条语句,使用连表的语句查询。FetchType.LAZY会产生多条语句,其实就是将连表的操作拆分了。
多对一
@ManyToOne用于标注多对一的关系。如下面多个员工对应一个部门。
在使用@ManyToOne注解的同时还需要添加@JoinColumn注解定义当前表中的外键名称。
修改关联对象
1.将一个员工调到另一个部门
@Test
public void t9() {
tx.begin();
Emp emp = em.find(Emp.class, 3L);
Dept dept = em.find(Dept.class, 21L);
emp.setDeptByDeptId(dept);
tx.commit();
}
2、通过级联删除部门及部门下的所有员工
@OneToMany添加属性cascade值为CascadeType.REMOVE。
删除部门
@Test
public void t10() {
tx.begin();
Dept dept = em.find(Dept.class, 21L);
em.remove(dept);
tx.commit();
}
七、JPQL(Java Persistence Query Language)Java持久化查询语言
JPQL是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语句绑定在一起,使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL
,其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
EntityManager的查询方法
获取EntityManager对象实例
EntityManagerFactory factory = Persistence.createEntityManagerFactory("wsl_JPA");
EntityManager em = factory.createEntityManager();
EntityManager实例的方法
方法 | 描述 |
---|---|
getResultList():List | 返回一个结果集 |
getSingleRestle() | 返回一个结果 |
setFirstResult(int startPosition) | 设置起始位置,在分页中使用 |
setMaxResults(int maxResult); | 设置返回的对象个数,在分页中使用 |
setParameter(String name,Obejct value) | 设置命名参数 |
setParameter(int position,Object value) | 设置索引参数,索引从0开始 |
executeUpdate:int | 执行insert、update和delete |
其中setMaxResults与setFirstResult方法使用链式编程。
因为JPQL采用的是ORM,对象关系映射。通过操作对象的方式实现对数据库表的访问。故执行JPQL语句是需要注意
表名对应的是类名,即在JPQL中使用类名来操作表
如查询EMP表信息JPQL的语句为:SELECT e From (类名)e
在JPQL不支持SELECT *
JPQL中的关键字和SQL一样,且不区分大小写。
参数绑定
索引绑定:?索引
命名参数::名称
例:
select e from Emp e where e.empno between ?1 and ?2
select e from Emp e where e.empno between :start and :end
投影查询
#返回结果集为Object[]的集合
select e.empno, e.ename, e.job from Emp e
#返回结果集为Emp对象的集合
select new Emp(e.empno, e.ename, e.job) from Emp e
EAGER查询与LARY查询方式
//懒查询
from Dept d
//查询本身及关联对象的属性
from Dept d inner join fetch d.emps
八、Spring Data JPA
1.按下列属性导入对应依赖
<properties>
<lo4j.version>1.2.17</lo4j.version>
<junit.version>4.12</junit.version>
<druid.version>1.2.6</druid.version>
<ojdbc6.version>11.2.0.4</ojdbc6.version>
<spring-jdbc.version>5.3.10</spring-jdbc.version>
<spring-data-jpa>2.5.6</spring-data-jpa>
<hibernate-core.version>5.6.1.Final</hibernate-core.version>
<spring-context.version>5.3.10</spring-context.version>
<spring-aspects.version>5.3.10</spring-aspects.version>
</properties>
2.配置Spring Data JPA
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--零配置方式实现Spring Dao方式-->
<context:component-scan base-package="com.example"/>
<!--读取数据库连接配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--一、XML配置Spring Dao的步骤-->
<!--1.配置数据源代理对象 LazyConnectionDataSourceProxy
实现了所有目标数据源的方法
只有在执行PreparedStatement的操作时,才会去获取连接,有效提高了连接的利用率。
-->
<!--数据源配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<!--连接池配置-->
<constructor-arg name="targetDataSource">
<bean class="com.alibaba.druid.pool.DruidDataSource"
p:driverClassName="${db.driver}"
p:url="${db.url}"
p:username="${db.username}"
p:password="${db.password}"
p:initialSize="${db.initSize}"
p:minIdle="${db.min}"
p:maxActive="${db.max}"/>
</constructor-arg>
</bean>
<jpa:repositories base-package="com.example.dao"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" p:dataSource-ref="dataSource" p:persistenceUnitName="wsl_JPA"/>
<bean id="transactionManagerJPA" class="org.springframework.orm.jpa.JpaTransactionManager"/>
<tx:annotation-driven transaction-manager="transactionManagerJPA"/>
</beans>
3.定义接口
package com.example.dao;
...
public interface EmpDao extends CrudRepository<Emp,Long> {
//使用JPA的预定义查询
List<Emp> queryEmpByEmpName(String empName);
//自定义查询
@Query("select e from Emp e where e.empName = :empName")
List<Emp> queryEmpByName(@Param("empName") String empName);
}
4.测试使用
public class HibernateSpring {
private static ApplicationContext context;
protected EmpDao empDao;
@BeforeClass
public static void init(){
context = new ClassPathXmlApplicationContext("hibernatebeans.xml");
}
@Before
public void before() {
empDao = context.getBean(EmpDao.class);
}
@Test
public void query17() {
List<Emp> emps = empDao.queryEmpByName("小明");
emps.forEach(out::println);
}
}