JPA的概念与Hibernate

JPA的概念与Hibernate

目录 JPA的概念与Hibernate一. JPA的概念二. Hibernate框架简介三. 基本操作1. 导入包2. 编写配置文件3. 编写代码4. Hibernate中对象的三种状态5. 一级缓存6. 二级缓存7. Hibernate中延迟加载8. Hibernate的关联关系9. Hibernate查询

一. JPA的概念

Java persistance API Java持久化API

官方对持久化操作提供的一套接口。包名以javax开头。

Hibernate是一套实现了JPA规范的持久化框架。

spring-data-jpa是spring框架中对JPA的实现。

二. Hibernate框架简介

Hibernate,翻译为冬眠,是一套全自动的ORM(对象关系映射object relation mapping)持久化框架。

优点:跨数据库。(代码的编写与数据库无关,很容易切换数据的版本和类型)。

   缓存机制。(一级缓存和二级缓存)。

   避免繁琐的sql语句的编写。

缺点:编写的语句不是sql语句,复杂的sql编写比较麻烦。

   由于需要转换成JDBC的语句,性能肯定比JDBC操作要低一些。



mysql数据库常见引擎种类:
1. MyISAM   在mysql5之前是默认引擎。
2. InnerDB  在mysql5之后是默认引擎。可以建立主外键,意味着性能会比MyISAM要低。
3. Memory   内存表,性能最高,但是直接放在内存中,消耗内存大。

三. 基本操作

  1. 导入包

    org.hibernate hibernate-core 5.2.10.Final
  2. 编写配置文件

全局的配置文件hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/j1901?useUnicode=true&amp;amp;characterEncoding=utf-8</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <!-- 方言(指定数据库) -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>

        <!-- 开发阶段开启 -->
        <!-- 打印生成后的sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 格式化打印的sql语句 -->
        <property name="hibernate.format_sql">true</property>


        <!-- 会自动生成数据库表 -->
        <!--
        create: 表示每次都会创建数据库里面的表
        create-drop: 表示每次都会创建数据库里面的表,每次停止的时候都会删除表
        update: 每次运行都会检查表结构,如果有不同则会修改表结构(开发阶段常用)
        validate: 每次都会检查表结构,如果有不同则抛出异常(一般在生产阶段设置)
        -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!--在同一个线程中共享session-->
        <property name="current_session_context_class">thread</property>

        <!-- 关联的映射文件 -->
        <mapping resource="Product.hbm.xml"></mapping>
    </session-factory>
</hibernate-configuration>

每个实体类对应的映射文件Product.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.qianfeng.day24.entity.Product" table="product">
        <id name="id" column="pro_id" type="java.lang.Integer">
            <!--
            native:让数据库自行处理
            assigned: 程序指定
            -->
            <generator class="native"></generator>
        </id>
        <property name="name" column="pro_name" type="java.lang.String" length="50"></property>
        <property name="price" column="pro_price" type="java.lang.Double"></property>
    </class>
</hibernate-mapping>
  1. 编写代码

    // 数据库的基本操作
    @Test
    public void test(){
    // 1. 读取配置文件
    Configuration configuration = new Configuration().configure();
    // 2. 得到sessionFactory
    SessionFactory sessionFactory = configuration.buildSessionFactory();
    // 3. 得到session
    Session session = sessionFactory.openSession();
    // 定义事务
    Transaction transaction = null;
    try {
    // 4. 开启事务
    transaction = session.beginTransaction();
    // 5. 数据库操作
    // 添加操作
    // Product product = new Product();
    // product.setName(“华为P20”);
    // product.setPrice(6000.0);
    // session.save(product);
    // 在添加的同时,如果表是自动增长,会获取id
    // System.out.println("===============" + product.getId());

             // 删除
    

    // Product product = new Product();
    // product.setId(2);
    // 建议先查询再删除
    // Product product = session.get(Product.class, 2);
    // if (product != null) {
    // session.delete(product);
    // }

             // 修改操作
             Product product = new Product();
             product.setId(1);
             product.setName("华为P30");
             product.setPrice(9999.0);
             session.update(product);
             // 6. 提交事务
             transaction.commit();
         }catch (Exception e){
             e.printStackTrace();
             // 回滚事务
             if (transaction != null){
                 transaction.rollback();
             }
         }finally {
             // 7. 关闭连接
             if (session != null) {
                 session.close();
             }
         }
     }
    
  2. Hibernate中对象的三种状态

在常见的操作过程中会出现的一些问题:

// 使用new的对象去修改或删除时,在操作之前查询了一次
session.get(Product.class, 1);

// 修改操作
Product product = new Product();
product.setId(1);
product.setName("华为P20");
product.setPrice(5999.0);
session.update(product);

出现如下异常:

org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.qianfeng.day24.entity.Product#1]...

hibernate中对象的三种状态:

Hibernate所管理的对象中会自动添加一个唯一的标识符OID。

瞬态(瞬时状态,临时状态):new出来的对象

持久态:与数据库中的数据有着强关联关系的状态。条件是session没有关闭。由于此状态数据与数据库有强关联,所以对此状态下的数据进行任何操作,在提交事务,或者关闭连接时都会提交到数据库中。

游离态(脱管态):当持久态数据被关闭连接时称为此状态。

上面错误代码可以修改如下:

// 使用new的对象去修改或删除时,在操作之前查询了一次
session.get(Product.class, 1);



// 修改操作
Product product = session.get(Product.class, 1);
product.setName("华为P20");
product.setPrice(5999.0);
transaction.commit(); // 或者使用session.close();(不建议使用close)
  1. 一级缓存

在Hibernate中,在同一个session中,如果多次查询某一个对象,只会查询一次,哪怕设置为null,也还是有效。例如:

 Product product1 = session.get(Product.class, 1);
product1 = null;
Product product2 = session.get(Product.class, 1);
Product product3 = session.get(Product.class, 1);
Product product4 = session.get(Product.class, 1);
Product product5 = session.get(Product.class, 1);

System.out.println("========"+ (product2 == product5));

// 结果为true,而且显示的sql只查询了一次

Hibernate中的一级缓存:是session级别的缓存,即只在session未关闭时有效,会将所有的持久态数据保存在内存中,在session未关闭前,再次查询该持久态数据时,不会真的去查询数据库,而是直接拿缓存中的对象使用,以减少数据库的查询频次,提升使用性能。默认使用,而且不能被关闭。

一级缓存几乎没有什么缺点,所以大胆使用,但是在批量添加时,需要手动清理缓存。

手动清理缓存的几种方式:

  1. session.close(); // 直接关闭连接,一级缓存自然被清理。

  2. session.clear(); // 表示将session中当前的缓存对象一次性清理。

  3. session.evict(obj); // 表示将obj对象从缓存中清理。
    实际上所谓一级缓存,就是在session对象中持有一个map属性,将所有的持久化对象放入到此map中,然后当需要查询缓存中数据时,就是去该map中查找,所谓清理缓存,就是清理该map中的数据。
    注意:Hibernate中的一级缓存与MyBatis中的一级缓存原理差不多,一样记忆。

  4. 二级缓存

二级缓存是SessionFactory级别缓存,或者叫全局范围的缓存。存入到二级缓存中的数据,除非服务器重启,否则一直存在,由于缓存是使用空间换时间的策略,所以二级缓存会大量的消耗内存。所以二级缓存并非默认开启,需要手动开启,而且二级缓存一般需要借助其他的第三方库,例如:EHCache, MemCache, Redis, MongoDB等。

因为二级缓存会大量的消耗内存,所以必须把项目需要的一些热门数据放到二级缓存中,不必要的数据不能放入,也就意味着,二级缓存需要有一个很好的管理策略(数据淘汰(驱逐)策略)。

缓存的雪崩和穿透。

  1. Hibernate中延迟加载

延迟加载,也叫做延时加载或懒加载。表示直到需要的时候才会真正的加载(查询)数据。

注意:在Hibernate中延迟加载的数据,一旦session关闭了,再去访问代理对象,会出现异常org.hibernate.LazyInitializationException: could not initialize proxy - no Session

在MyBatis中,session即使关闭了,还会再次打开并查询,然后关闭。

get方法和load方法的区别:

  1. get是直接加载,而load是懒加载。

  2. 如果没有查询到结果,那么get将返回一个null,而load直接抛出异常。

  3. Hibernate的关联关系

多对一

一对多

多对多

一对一

单向和双向:单向的意思就是在一方的实体类上描述关联关系,双向的意思是在双方实体类上描述关联关系。

双向一对多(双向多对一)示例如下:

@Getter
@Setter
public class Product {
    private Integer id;
    private String name;
    private Double price;
    private ProductType type;
}

@Getter
@Setter
public class ProductType {
    private Integer id;
    private String name;
    private Set<Product> products = new HashSet<>(0);
}

Product.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.qianfeng.day24.entity.Product" table="product">
        <id name="id" column="pro_id" type="java.lang.Integer">
            <!--
            native:让数据库自行处理
            assigned: 程序指定
            -->
            <generator class="native"></generator>
        </id>
        <property name="name" column="pro_name" type="java.lang.String" length="50"></property>
        <property name="price" column="pro_price" type="java.lang.Double"></property>
        <many-to-one name="type" column="type_id" class="com.qianfeng.day24.entity.ProductType"></many-to-one>
    </class>
</hibernate-mapping>

ProductType.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.qianfeng.day24.entity.ProductType" table="product_type">
        <id name="id" column="type_id" type="java.lang.Integer">
            <!--
            native:让数据库自行处理
            assigned: 程序指定
            -->
            <generator class="native"></generator>
        </id>
        <property name="name" column="type_name" type="java.lang.String" length="50"></property>
        <set name="products">
            <key column="type_id"></key>
            <one-to-many class="com.qianfeng.day24.entity.Product"></one-to-many>
        </set>
    </class>
</hibernate-mapping>

一对多关联下的数据操作:

添加:

ProductType type = new ProductType();
type.setName("手机");
session.save(type);

Product product = new Product();
product.setName("P20");
product.setPrice(5999.0);
product.setType(type);
session.save(product);

修改:

// 查询修改后的类型
ProductType type = session.get(ProductType.class, 2);
// 查询当前要修改的产品信息
Product product = session.get(Product.class, 3);
// 设置类型
product.setType(type);
// 不需要调用update方法,只需要提交事务就会自动修改到数据库中

删除:

// 对于其他表没有关联影响的数据,直接使用单表操作删除即可
Product product = session.get(Product.class, 3);
session.delete(product);

// 如果与其他表数据有关联,删除时可能产生异常
ProductType type = session.get(ProductType.class, 2);
session.delete(type);
// 异常如下:Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'type_id' cannot be null

inverse关键字和cascade的作用:

inverse:反转

// 上面的删除语句在 一方的关系配置中配置了inverse=true的时候,出现异常:
// com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails 
// 当值为false的时候
// false为默认值,还是引发上面的cannot be null异常
// 此处的反转含义是:是否由对方来处理关联关系。当设置为true时,产品表会因为有外键关联而不允许删除,当设置为false时,产品类型表会断开关系,设置产品表外键为null,然后删除当前记录。
// 一般情况下,会将此值设置为true

cascade:级联

delete: 删除时级联
save-update:添加或修改时级联
none:都不级联
all:全部级联

<!-- 设置级联删除 -->
<set name="products" inverse="true" cascade="delete">
  <key column="type_id"></key>
  <one-to-many class="com.qianfeng.day24.entity.Product"></one-to-many>
</set>
  1. Hibernate查询

Hibernate中查询分为三种方式:

  1. HQL(Hibernate Query Language)Hibernate查询语言

    // 必须使用类名和属性名,不能使用表名和字段名
    // 1. 基本查询
    // List list = session.createQuery(“from Product”, Product.class).list();
    // for (Product product : list) {
    // System.out.println(product);
    // }
    // 2. 条件查询
    // List list = session.createQuery(“from Product where price = ?”, Product.class)
    // .setParameter(0, 3999.0)
    // .list();
    // for (Product product : list) {
    // System.out.println(product);
    // }
    // 3. 具名查询
    // List list = session.createQuery(“select t from Product t where price = :p”, Product.class)
    // .setParameter(“p”, 3999.0)
    // .list();
    // for (Product product : list) {
    // System.out.println(product);
    // }

             // 4. 部分字段查询
    

    // List<Object[]> list = session.createQuery(“select t.id, t.name from Product t where price = :p”, Object[].class)
    // .setParameter(“p”, 3999.0)
    // .list();
    // for (Object[] product : list) {
    // for (Object o : product) {
    // System.out.println(o);
    // }
    // }
    // 5. 查询部分字段并封装字段
    // List list = session.createQuery(“select new Product(t.id, t.name) from Product t where price = :p”, Product.class)
    // .setParameter(“p”, 3999.0)
    // .list();
    // for (Product product : list) {
    // System.out.println(product);
    // }

             // 6. 函数查询,并返回单条结果
    

    // Long count = session.createQuery(“select count(1) from Product t”, Long.class)
    // .uniqueResult();
    // System.out.println(count);

             // 7. 分页并排序查询
    

    // List list = session.createQuery(“from Product t order by t.price desc”, Product.class)
    // .setFirstResult(2) // 跳过几条
    // .setMaxResults(2) // 显示几条
    // .list();
    // for (Product product : list) {
    // System.out.println(product);
    // }

             // 8. 关联查询
    

    // SELECT p.* FROM product p INNER JOIN product_type t ON p.type_id = t.type_id WHERE t.type_name = ‘华为手机’

    // List list = session.createQuery(“SELECT p FROM Product p INNER JOIN ProductType t ON p.type.id = t.id WHERE t.name = ‘华为手机’”, Product.class).list();
    // for (Product product : list) {
    // System.out.println(product);
    // }

             // 简化1
    

    // List list = session.createQuery(“SELECT p FROM Product p INNER JOIN ProductType t ON p.type = t WHERE t.name = ‘华为手机’”, Product.class).list();
    // for (Product product : list) {
    // System.out.println(product);
    // }

             // 简化2
    

    // List list = session.createQuery(“SELECT p FROM Product p WHERE p.type.name = ‘华为手机’”, Product.class).list();
    // for (Product product : list) {
    // System.out.println(product);
    // }

  2. Criteria 面向对象的查询
    List list = session.createCriteria(Product.class)
    .add(Restrictions.eq(“price”, 3999.0))
    .list();
    for (Product product : list) {
    System.out.println(product);
    }

  3. Native Query( SQL query)原生的SQL查询

    List list = session.createNativeQuery(“SELECT p.* FROM product p INNER JOIN product_type t ON p.type_id = t.type_id WHERE t.type_name = ‘华为手机’”, Product.class).list();
    for (Product product : list) {
    System.out.println(product);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值