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主要解决对象-关系的映射。

面向对象概念

面向关系概念



对象

表的行(记录)

属性

表的列(字段)

~ 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中连接数据库

javaassist api文档_javaassist api文档


四、添加JPA模块及在项目resources\META-INF目录下加入persistence.xml配置文件,provider选择Hibernate。

javaassist api文档_sql_02


五、编辑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窗口,右键单机项目工件,选择生成持久性映射,通过数据库框架。

javaassist api文档_hibernate_03


下面是我生成的实体类,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集合。

javaassist api文档_数据库_04


@OneToMany的mappedBy属性值为此关系的另一端的关联属性名。fetch为获取数据的方式,值为枚举类FetchType下的属性。FetchType.EAGER只产生一条语句,使用连表的语句查询。FetchType.LAZY会产生多条语句,其实就是将连表的操作拆分了。多对一

@ManyToOne用于标注多对一的关系。如下面多个员工对应一个部门。

javaassist api文档_数据库_05


在使用@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。

javaassist api文档_sql_06


删除部门

@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);
    }
}