JPA与hibernate-------JPA

Posted 雨听风说

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JPA与hibernate-------JPA相关的知识,希望对你有一定的参考价值。

ORM概述

ORM(Object-Relational Mapping) 表示对象关系映射。在面向对象的软件开发中,通过ORM,就可以把对象映射到关系型数据库中。只要有一套程序能够做到建立对象与数据库的关联,操作对象就可以直接操作数据库数据,就可以说这套程序实现了ORM对象关系映射

简单的说:ORM就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。

为什么要使用ORM

当实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存数据、修改数据、删除数据,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射。

常见的ORM框架

Mybatis(ibatis)、hibernate、Jpa

 hibernate与JPA的概述

hibernate概述

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。

JPA概述

JPA的全称是Java Persistence API, 即Java 持久化API,是SUN公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。

 

JPA通过JDK 5.0注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

JPA的优势

1. 标准化

   JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。

 

2. 容器级特性的支持

   JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。

 

3. 简单方便

   JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成

 

4. 查询能力

   JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。

 

5. 高级特性

   JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

 JPA与hibernate的关系

 

JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。           

 

JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。JPA怎么取代Hibernate呢?JDBC规范可以驱动底层数据库吗?答案是否定的,也就是说,如果使用JPA规范进行数据库操作,底层需要hibernate作为其实现类完成数据持久化工作。

案例介绍

此处使用hibernate 5.4.10.Final   maven 3.6.3

 导入依赖

 <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>5.4.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

 

创建配置文件persistence.xml

注意:配置文件的目录

 

在java工程的src路径下创建一个名为META-INF的文件夹,在此文件夹下创建一个名为persistence.xml的配置文件

 

 

 

 

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">

    <!--
        需要配置persistence-unit 节点
        持久化单元
            name : 持久化单元名称
            transaction-type : 事务管理方式
                RESOURCE_LOCAL : 本地事务管理
                JTA : 分布式事务管理
     -->
    <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
        <!-- jpa的实现方式 -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <!--
                配置数据库的信息
                用户名 : javax.persistence.jdbc.user
                密码 : javax.persistence.jdbc.password
                连接地址 : javax.persistence.jdbc.url
                驱动 : javax.persistence.jdbc.driver
             -->
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>

            <!--配置jpa实现方(hibernate)的配置信息
               显示sql           :   false|true
               自动创建数据库表    :  hibernate.hbm2ddl.auto
                       create      : 程序运行时创建数据库表(如果有表,先删除表再创建)
                       update      :程序运行时创建表(如果有表,不会创建表)
                       none        :不会创建表

           -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
        
    </persistence-unit>

</persistence>

 

 

创建实体类

package com.ytfs.entity;


import javax.persistence.*;
import java.io.Serializable;

/**
 * @Classname Customer
 * @Description TODO(客户实体类)
 * @Date 2020/4/28 22:34
 * @Created by ytfs
 *  客户的实体类
 *     配置映射关系
 *  1.实体类和表的映射关系
 *     @Entity:声明实体类
 *     @Table : 配置实体类和表的映射关系
 *         name : 配置数据库表的名称
 *  2.实体类中属性和表中字段的映射关系
 *
 */

@Entity
@Table(name = "cst_customer")
public class Customer implements Serializable {
    /**
     * @Id:声明主键的配置
     * @GeneratedValue:配置主键的生成策略
     *      strategy
     *          GenerationType.IDENTITY :自增,mysql
     *                 * 底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
     *          GenerationType.SEQUENCE : 序列,oracle
     *                  * 底层数据库必须支持序列
     *          GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
     *          GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略
     * @Column:配置属性和字段的映射关系
     *      name:数据库表中字段的名称
     */
    @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_address")
    private String custAddress;

    @Column(name = "cust_phone")
    private String custPhone;

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }

    public String getCustIndustry() {
        return custIndustry;
    }

    public void setCustIndustry(String custIndustry) {
        this.custIndustry = custIndustry;
    }

    public String getCustLevel() {
        return custLevel;
    }

    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }

    public String getCustAddress() {
        return custAddress;
    }

    public void setCustAddress(String custAddress) {
        this.custAddress = custAddress;
    }

    public String getCustPhone() {
        return custPhone;
    }

    public void setCustPhone(String custPhone) {
        this.custPhone = custPhone;
    }


    @Override
    public String toString() {
        return "Customer{" +
                "custId=" + custId +
                ", custName=\'" + custName + \'\\\'\' +
                ", custSource=\'" + custSource + \'\\\'\' +
                ", custIndustry=\'" + custIndustry + \'\\\'\' +
                ", custLevel=\'" + custLevel + \'\\\'\' +
                ", custAddress=\'" + custAddress + \'\\\'\' +
                ", custPhone=\'" + custPhone + \'\\\'\' +
                \'}\';
    }
}

参数的解释

        @Entity
            作用:指定当前类是实体类。
        @Table
            作用:指定实体类和表之间的对应关系。
            属性:
                name:指定数据库表的名称
        @Id
            作用:指定当前字段是主键。
        @GeneratedValue
            作用:指定主键的生成方式。。
            属性:
                strategy :指定主键生成策略。
        @Column
            作用:指定实体类属性和数据库表之间的对应关系
            属性:
                name:指定数据库表的列名称。
                unique:是否唯一  
                nullable:是否可以为空  
                inserttable:是否可以插入  
                updateable:是否可以更新  
                columnDefinition: 定义建表时创建此列的DDL  
                secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字搭建开发环境[重点]

 

创建JPA的连接工具类

package com.ytfs.utils;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 * @Classname Jpautil
 * @Description TODO(Jpa实体类管理工厂)
 * @Date 2020/4/28 23:38
 * @Created by ytfs
 */
public class JpaUtil {

    //线程安全的
    private static final EntityManagerFactory FACTORY;

    static {

        /*这里的实体类工厂的persistenceUnitName是配置文件中
         <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
         */
        FACTORY = Persistence.createEntityManagerFactory("myJpa");

    }

    /**
     * 返回实体类管理对象
     * @return
     */
    public static EntityManager getEM(){
        return FACTORY.createEntityManager();
    }
}

创建测试类

package com.ytfs;

import com.ytfs.entity.Customer;
import com.ytfs.utils.JpaUtil;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

/**
 * @Classname test
 * @Description TODO(JPA测试)
 * @Date 2020/4/28 22:58
 * @Created by ytfs
 */


public class test {

    /**
     * 保存客户
     */
    @Test
    public void testSave() {
       /* //创建实体类的工厂管理对象
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");

        //创建实体类管理对象
        EntityManager entityManager = factory.createEntityManager();*/

        EntityManager entityManager = JpaUtil.getEM();
        //开启事务
        entityManager.getTransaction().begin();
        //创建一个对象
        Customer customer = new Customer();
        customer.setCustName("张三");
        customer.setCustAddress("重庆市沙坪坝区");
        //保存对象
        entityManager.persist(customer);

        //提交事务
        entityManager.getTransaction().commit();

        //释放资源
        entityManager.close();
        //factory.close();   利用工具类之后就不用关闭工厂对象,因为线程安全只需要一个,后面的还会一直用
    }


    /**
     * @throws
     * @description find方式通过Id查询
     * @author 雨听风说
     * @updateTime 2020/4/29 13:03
     * 使用find方法查询:
     * 1.查询的对象就是当前客户对象本身
     * 2.在调用find方法的时候,就会发送sql语句查询数据库
     * <p>
     * 立即加载
     */
    @Test
    public void testFind() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //获取事务对象
        EntityTransaction tx = em.getTransaction();
        //开启事务
        tx.begin();

        //通过实体类管理对象查询
        Customer customer = em.find(Customer.class, 1L);

        //customer.soutv
        System.out.println("customer = " + customer);

        //提交事务
        tx.commit();

        //释放资源
        em.close();
    }


    /**
     * @param
     * @return
     * @throws
     * @description 通过事务管理对象的get方式通过Id查询
     * @author 雨听风说
     * @updateTime 2020/4/29 13:13
     * getReference方法
     * 1.获取的对象是一个动态代理对象
     * 2.调用getReference方法不会立即发送sql语句查询数据库
     * * 当调用查询结果对象的时候,才会发送查询的sql语句:什么时候用,什么时候发送sql语句查询数据库
     * <p>
     * 延迟加载(懒加载)
     * * 得到的是一个动态代理对象
     * * 什么时候用,什么使用才会查询
     */

    @Test
    public void testGetRefrence() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //获取事务对象并开启事务
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        //通过实体类对象查询
        Customer customer = em.getReference(Customer.class, 1L);

        System.out.println("customer = " + customer);

        //提交事务
        tx.commit();

        //释放资源
        em.close();
    }

    /**
     * @param
     * @return
     * @throws
     * @description 删除对象
     * @author 雨听风说
     * @updateTime 2020/4/29 13:17
     */
    @Test
    public void testRemove() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //获取事务对象并开启事务
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        //通过实体类对象删除
        //i   通过id查询需要删除的对象
        Customer customer = em.find(Customer.class, 1L);
        //ii  调用remove方法,传入需要删除的对象
        em.remove(customer);
        //提交事务
        tx.commit();

        //释放资源
        em.close();


    }
    /**
     * @param
     * @return
     * @throws
     * @description 更新对象
     * @author 雨听风说
     * @updateTime 2020/4/29 13:17
     */
    @Test
    public void testUpdate() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //获取事务对象并开启事务
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        //通过实体类对象更新
        //i   通过id查询需要更新的对象
        Customer customer = em.find(Customer.class, 2L);
        customer.setCustName("电动小马达");

        //ii  调用merge方法,传入需要更新的对象
        em.merge(customer);

        //提交事务
        tx.commit();

        //释放资源
        em.close();
    }
}

JPQL查询(Java Persistence Query Language)

基于首次在EJB2.0中引入的EJB查询语言(EJB QL),Java持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL。

 

其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。

测试

package com.ytfs;

import com.ytfs.utils.JpaUtil;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.List;

/**
 * @Classname JpqlTest
 * @Description TODO(Jpql的测试案例)
 * @Date 2020/4/29 13:22
 * @Created by ytfs
 */


public class JpqlTest {

    /**
     * jpql查询全部
     * jqpl:from com.ytfs.entity.Customer
     * sql:SELECT * FROM cst_customer
     */
    @Test
    public void testFindAll() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //通过实体类管理对象获取事务对象并开启事务
        em.getTransaction().begin();

        //书写jpql语句
        String jpql = "from Customer";

        //通过实体类管理对象执行查询
        Query query = em.createQuery(jpql);

        //获取查询结果
        List list = query.getResultList();

        //遍历输出
        list.stream().forEach(System.out::println);

        //提交事务
        em.getTransaction().commit();

        //释放资源
        em.close();

    }


    /**
     * 排序查询: 倒序查询全部客户(根据id倒序)
     * sql:SELECT * FROM cst_customer ORDER BY cust_id DESC
     * jpql:from Customer order by custId desc
     *
     * 进行jpql查询
     * 1.创建query查询对象
     * 2.对参数进行赋值
     * 3.查询,并得到返回结果
     */
    @Test
    public void test() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //通过实体类管理对象获取事务对象并开启事务
        em.getTransaction().begin();

        //书写jpql语句
        String jpql = "from Customer order by custId desc";

        //通过实体类管理对象执行查询
        Query query = em.createQuery(jpql);

        //获取查询结果
        List list = query.getResultList();

        //遍历输出
        list.stream().forEach(System.out::println);

        //提交事务
        em.getTransaction().commit();

        //释放资源
        em.close();

    }

    /**
     * 使用jpql查询,统计客户的总数
     * sql:SELECT COUNT(cust_id) FROM cst_customer
     * jpql:select count(custId) from Customer
     */
    @Test
    public void testCount() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //通过实体类管理对象获取事务对象并开启事务
        em.getTransaction().begin();

        //书写jpql语句
        String jpql = "select count(custId) from Customer";

        //通过实体类管理对象执行查询
        Query query = em.createQuery(jpql);

        //ii.对参数赋值
        //iii.发送查询,并封装结果

        /**
         * getResultList : 直接将查询结果封装为list集合
         * getSingleResult : 得到唯一的结果集
         */
        Object singleResult = query.getSingleResult();

        //遍历输出
        System.out.println("singleResult = " + singleResult);

        //提交事务
        em.getTransaction().commit();

        //释放资源
        em.close();

    }

    /**
     * 分页查询
     * sql:select * from cst_customer limit 0,2
     * jqpl : from Customer
     */

    @Test
    public void testByPage() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //通过实体类管理对象获取事务对象并开启事务
        em.getTransaction().begin();

        //书写jpql语句
        String jpql = "from Customer";

        //通过实体类管理对象执行查询
        Query query = em.createQuery(jpql);

        //ii.对参数赋值
        //开始的索引
        query.setFirstResult(0);
        //每页的条数
        query.setMaxResults(2);
        //iii.发送查询,并封装结果

        /**
         * getResultList : 直接将查询结果封装为list集合
         * getSingleResult : 得到唯一的结果集
         */
        //获取查询结果
        List list = query.getResultList();

        //遍历输出
        list.stream().forEach(System.out::println);

        //提交事务
        em.getTransaction().commit();

        //释放资源
        em.close();

    }

    /**
     * 条件查询
     * 案例:查询客户名称包含‘小马’的客户
     * sql:SELECT * FROM cst_customer WHERE cust_name LIKE  ?1
     * jpql : from Customer where custName like ?1
     */

    @Test
    public void testByCondition() {

        //通过工具类获取实体类管理对象
        EntityManager em = JpaUtil.getEM();

        //通过实体类管理对象获取事务对象并开启事务
        em.getTransaction().begin();

        //书写jpql语句
        String jpql = "from Customer where custName like ?1 ";

        //通过实体类管理对象执行查询
        Query query = em.createQuery(jpql);

        //ii.对参数赋值
        //第一个参数:占位符的索引位置(jpql语句后的数字代表,直接问号出现异常),第二个参数:取值
        query.setParameter(1, "%小马%");
        //iii.发送查询,并封装结果

        /**
         * getResultList : 直接将查询结果封装为list集合
         * getSingleResult : 得到唯一的结果集
         */
        //获取查询结果
        List list = query.getResultList();

        //遍历输出
        list.stream().forEach(System.out::println);

        //提交事务
        em.getTransaction().commit();

        //释放资源
        em.close();

    }
}

 

以上是关于JPA与hibernate-------JPA的主要内容,如果未能解决你的问题,请参考以下文章

JPA与hibernate-------JPA

Spring Security 3 身份验证与 Hibernate 3(JPA) 注释的集成

将 Java UUID 存储在二进制数据库列中,Hibernate/JPA 与原始 JDBC

Hibernate JPA双向一对多结果与约束冲突异常

hibernate/jpa double OneToOne 与一个实体的双向关系

Hibernate/JPA @OneToOne 返回空指针异常