Hibernate

一、认识 Hibernate
Hibernate是开放源代码的ORM(对象关系映射)框架。是持久层的全自动的ORM框架。

1、特点:

1、 将对数据库的操作转换为对Java对象的操作,从而简化开发。通过修改一个“持久化”对象的属性从而修改数据库表中对应的记录数据。
2、 提供线程和进程两个级别的缓存提升应用程序性能。
3、有丰富的映射方式将Java对象之间的关系转换为数据库表之间的关系。
4、屏蔽不同数据库实现之间的差异。在Hibernate中只需要通过“方言”的形式指定当前使用的数据库,就可以根据底层数据库的实际情况生成适合的SQL语句。
5、非侵入式:Hibernate不要求持久化类实现任何接口或继承任何类,POJO即可。

二、核心API:
有6个核心的类:Configuration 、SessionFactory 、Session 、Transaction 、Query 、 Criteria

二、基本配置
1、所需要包:下载的hibernate文件中lib/requid下的所有jar包。
2、创建表。
3、为表创建对应持久化类。
4、创建类与表的xml映射文件并进行配置。格式:类名.hbm.xml一般与类同级

<!--在导入的hibernate-core.jar包下的map.dtd文件中可以找到-->
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<!--ORM 对象关系映射-->
<hibernate-mapping>

<!--如果表名和类名一致,table可以省略   如果表字段名和类属性名一致,表字段名可以省略-->
<!--建立类和表的映射关系-->
<class name="com.ly.hibernate.domain.Customer" table="customer">
    <!--表的主键字段与类的属性映射-->
    <id name="id" column="id">
        <!--主键生成策略-->
        <generator class="native"/>
    </id>

    <!--表的其他字段与类的属性映射-->
    <property name="name" column="name"/>
    <property name="email" column="email"/>
    <property name="phone" column="phone"/>
    <property name="address" column="address"/>
</class>

5、配置Hibernate核心配置文件。hibernate.cfg.xml。一般位于src根目录下

<?xml version='1.0' encoding='utf-8'?>

<!--在导入的hibernate.core.jar包下的configuration.dtd可以找到-->
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<!--hibernate核心配置文件-->
<hibernate-configuration>
    <!--一个数据库一个SessionFactory-->
    <session-factory>
        <!--连接数据库的基本参数-->
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">ly199811165218</property>
        <!--配置hibernate识别的是哪一种数据库的方言(语法)-->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

    <!--可以指定在执行过程中打印sql语句  默认是不打印的-->
    <property name="hibernate.show_sql">true</property>

    <!--格式化打印sql-->
    <property name="hibernate.format_sql">true</property>
    
    <!--自动生成DDL语句-->
    <property name="hibernate.hbm2ddl.auto">update</property>

    <!--配置对象关系映射配置文件的位置   注意:这里使用的是/ -->
    <mapping resource="com/ly/hibernate/domain/Customer.hbm.xml" />
</session-factory>

hibernate.hbm2ddl.auto的值:
update
最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等 应用第一次运行起来后才会。
create
每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
create-drop
每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
validate
每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

三、基本使用
所有操作都是相对于hibernate中的session来使用的。

1、添加

 save方法

2、查询

   get或load方法  两者有区别

3、修改

update方法

4、删除

delete方法

get与load方法的区别:
1、get方法:
*采用的是立即加载,执行该方法时会马上发送SQL到数据库中去查询。
* 查询后返回的是真实的对象本身
* 查询一个不存在的对象时,返回null
*
2、 load方法:
*采用的延迟加载(lazy),执行该方法时,不会发送SQL,而要等到真正使用该对象时才会发送
* 查询后返回的是一个代理对象,代理机制使用的是 javassist 包中的技术
* 查询一个找不到的对象时,抛出ObjectNotFoundException

Hibernate工具类:

public class HibernateUtils {
    private static final SessionFactory sessFactory;

static{
    try{
        Configuration configuration=new Configuration();
        configuration.configure();  //加载核心配置文件,加载映射
        sessFactory=configuration.buildSessionFactory();    //创建sess工厂
    }catch (Throwable ex){
        throw new ExceptionInInitializerError(ex);
    }

}

//获取session,hibernate中的session是非线程安全的
public static Session getSession(){
    return sessFactory.openSession();
}

}

四、什么是持久化类?**
1、持久化:将内存中的一个对象持久化到数据库中的过程。Hibernate框架是用来进行持久化的框架。
2、持久化类:一个Java类与数据库中的表建立了映射关系,那么这个类在Hibernate中称为持久化类。
3、持久化类的编写规则:
(1)、持久化类需要有无参构造器。 Hibernate底层需要使用反射生成实例。
(2)、属性需要私有,对私有属性提供public的get和set方法。
(3)、对持久化类提供一个唯一标示OID与表的主键相对应。
(4)、持久化类中的属性的类型尽量使用包装类类型。
(5)、持久化类不要使用final修饰。

注意:
1、Java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一个记录。在Hibernate中通过持久类的OID属性区分是否是同一个对象。
2、延迟加载是Hibernat的一种优化手段。返回的是一个代理对象(javassist可以为没有实现接口的类产生代理–使用的是一种字节码增强技术,继承这个类进行代理)。如果使用final修饰,就不能被继承,也就不能产生代理,延迟加载也会失效,get和load方法也就一致了。

五、主键生成策略
1、主键分类:
(1)自然主键:主键的本身就是表中的一个字段(实体中的一个具体的属性,如人的省份证号)。
(2)代理主键:主键的本身不是表中必须的一个字段(不是实体中的具体属性)。

注意:
1、在实际开发中,尽量使用代理主键。
2、如果使用自然主键,自然主键参与到了业务逻辑中,后期有可能需要修改源代码。
3、好的程序设计应该满足OCP原则,对程序的扩展是Open的,对修改源码是Close的。

2、主键生成策略:
在Hibernate中为了减少程序编写,提供了多种主键的生成策略(常用的)。
(1)incrementhibernate中提供的自动增长机制,适用short、int、long类型的主键。存在线程安全问题,用于单线程中。

首先发送一条语句查询当前最大id(select max(id) from 表),然后将id加1作为下一条记录的主键

(2)identity使用的是数据库底层中的自动增长机制。适用short、int、long类型的主键。是线程安全的。但只能用于有自动增长机制的数据库(如:Mysql、sql server)

(3) sequence采用的是序列的方式。适用short、int、long类型的主键。(Oracle支持序列)

(4) uuid使用hibernate中的随机方式生成字符串主键,适用于字符串类型的主键。

(5) native :本地策略,可以在identity和sequence之间进行自动切换

(6) assigned :由用户生成主键值,并且要在save()之前指定否则会抛出异常。主键的生成值完全由用户决定,与底层数据库无关。用户需要维护主键值,在调用session.save()之前要指定主键值。

六、持久化类的三种状态
1、瞬时态:
指我们 new 出来的对象,它不存在 OID,与 hibernate session 无关联,在数据库中也无记录。它使用完成后,会被 jvm 直接回收掉,它只是用于信息携带。

2、持久态:
在 hibernate session 管理范围内,它具有持久化标识 OID 它的特点,在事务未提交前一直是持久态,当它发生改变时, hibernate 是可以检测到的,存在自动更新的能力(原理是一级缓存)

3、游离态:
指持久态对象失去了与 session 的关联,托管态对象它存在 OID,在数据库中有可能存在,也有可能不存在。对于托管态对象,它发生改变时 hibernet 不能检测到。

在这里插入图片描述

七、Hibernate的一级缓存

缓存,是一种优化方式。是将某些数据存放在内存中,使用时直接从缓存中获取,不用再去数据源获取。

Hibernate框架提供了优化手段: 缓存、抓取策略。
Hibernate提供了两种缓存机制:一级缓存、二级缓存。

一级缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预。是由一系列Java集合构成的,只要session没有关闭就会一直存在。
1、当应用程序调用Session的save()、update()、saveOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate()或filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。
2、当commit()提交事务时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。
3、Session为应用程序提供了两个管理缓存的方法: evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象。close()会关闭session,也就关闭了缓存。

一级缓存 作用:减少对数据库的访问次数。

二级缓存是SessionFactory级别的缓存,它是属于进程范围或集群范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。 Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存。(一般不使用了,使用Redis了)

持久态对象自动更新的原理(一级缓存):
在创建session时,会创建一个缓存区(保存持久化对象)和一个快照区(保存的是持久化对象的一个副本)。
当进行事务提交时,会比较缓存区和快照区中的对象,如果对象发生了改变,则会发送SQL语句同步到数据库中。

八、Hibernate事务
事务:指逻辑上的操作,组成这组操作的逻辑单元要么全部成功,要么全部失败。

1、事务特性:
(1)原子性:代表事务不可分割。
(2)一致性:代表事务执行前后,数据的完整性保持一致。
(3)隔离性:代表一个事务执行的过程中,不应该受到其他事务的干扰。
(4)持久性:代表事务执行完成后,数据持久到数据库中。

2、如果不考虑隔离性,会引发的安全问题:
读问题:

  • 脏读。一个事务读到另一个事务尚未提交的事务
  • 不可重复读。一个事务读到另一个事务已经提交的update数据,导致前一个数据多次查询结果不一致。
  • 虚读。一个事务读到另一个事务已经提交的insert数据,导致前一个数据多次查询结果不一致。

写问题

  • 引发两类丢失更新。

3、读问题的解决,设置事务的隔离性:

  • Read Uncommited(读未提交) 读问题都不能解决
  • Read Commited (读已提交) 可以解决脏读。Oracle默认。
  • Repeatable Read (可重复读)解决脏读和不可重复读。Mysq默认。
  • Serializable (串行化)解决所有读问题。(也就是不允许事务并发,效率低)。

4、Hibernate中设置事务的隔离级别
通过在核心配置文件中设置。

<property name="hibernate.connection.isolation"> 4 </property>

1  : Read Uncommited  isolation
2  : Read Commited  isolation
4  : Repeatable Read  isolation
8  : Serializable  isolation

注意:事务一般放在 service层,service中封装业务逻辑操作(可能会操作多个DAO),而DAO层封装的是对数据源的单个操作。

service层事务保证使用的是同一个连接对象的方法:
1、向下传递。在service层获取连接对象,传递给DAO层的方法。如DBUtils。
2、使用ThreadLocal。将当前同一个连接对象与当前线程绑定,在DAO中通过当前线程来获取该连接对象。

5、Hibernate的getCurrentSession方法
为了保证事务使用的是同一个连接,Hibernate内部已经是实现了ThreadLocal,提供了getCurrentSession()方法。(默认该方法不能用,需要在核心配置文件中进行配置。)

<property name="hibernate.current_session_context_class"> thread </property>

此时session不需要手动关闭,线程结束就会自动关闭session。

public class HibernateUtils {
    private static final SessionFactory sessFactory;

static{
    try{
        Configuration configuration=new Configuration();
        configuration.configure();  //加载核心配置文件,加载映射
        sessFactory=configuration.buildSessionFactory();    //创建sess工厂
    }catch (Throwable ex){
        throw new ExceptionInInitializerError(ex);
    }

}

//获取session,hibernate中的session是非线程安全的
public static Session getSession(){
    return sessFactory.openSession();
}

//获取与线程绑定的session
public static Session getCurrentSession(){
    return sessFactory.getCurrentSession();
}

}

九、Hibernate中 一对多、多对多、一对一关系
1、基本关系:
(1)一对多(多对一):
学校与学生之间的关系是一对多的关系。一个学校对应多一个学生,一个学生对应一个学校。

(2)多对多:
学生与课程之间的关系是多对多的关系。一个课程对应多个学生,一个学生对应多门课程。

注意:多对多的关系一般需要借助 中间表 来构建表。

(3)一对一:
一个学生对应一个学号,一个学号对应一个学生。一多一的关系可以转成一张表表示。

2、Hibernate中一对多的关系

  • 一的一方的持久化类中应该有多的一方的Set集合,并提供get和set方法。

  • 多的一方的持久化类中应该有一的一方的对象的引用,并提供get和set方法(不需要与外键同名的属性)。

  • 在一的一方的映射文件中添加一多对的关系:

     < !--
                  配置一对多的关系
                  name : 集合的名称
                  column : 多的一方表中的外键名称
                  class : 多的一方的类的全路径
                  cascade : 级联保存或修改(只保存或修改一的一方,多的一方也会相应保存或修改)
              -- >
          < set name="students" cascade="save-update">
              <key column="sc_id"/>
              <one-to-many class="com.ly.hibernate.domain.Student"/>
          < /set>
    
  • 在多的一方的映射文件中添加多对一的关系:

    < !--
              配置多对一的关系
              name  :  一的一方的对象名称
              class :  一的一方的持久化类的全路径名称
              column : 多的一方表中对应的外键名称
               cascade : 级联保存或修改(只保存或修改一的一方,多的一方也会相应保存或修改)
          -->
    
      <!--配置级联保存或更新-->
      < many-to-one name="school" class="com.ly.hibernate.domain.School" column="sc_id" cascade="save-update"/>
    

(1)一对多的级联保存或删除:
也就是当一方进行保存或删除时,另一方也会进行相应操作。配置cascade="save-update"

(2) 一对多的删除操作:
默认情况下:删除一的一方,会先将多的一方的外检设置为null,然后在删除一的一方
配置级联删除 :在一的一方配置 cascade="delete",删除一的一方时,同时会删除与其有关的多的一方。(cascade有多个配置时,可以用 , 隔开)

(3)双向维护会发送多余的SQL语句,处理方法:
进行单项维护。
也可以让一的一方放弃外键维护权,在一的一方的配置文件添加 inverse="true"

十、Hibernate中的查询方式

1、OID查询
根据OID(主键)来检索。

  • get方法:User user=session.get(User.class , 1);
  • load方法 :User user=session.get(User.class , 1);

2、对象导航检索
根据一个已经查询到的对象,获取到其关联的对象。
在一对多的关系中(如学生和学校):

School school=session.get(School.class , 1);
List< Student>  students=school.getStudents();

3、HQL检索
HQL(Hibernate Query Language)是Hibernate中的一种面向对象的查询方式。

(1)普通查询

 Query< User> query = session.createQuery("from User");	//注意from后面是类名。
 List< User> users = query.list();
   
 Query query=session.queryQuery("select count(*) from User");	// *可以使用在聚合函数中,但不能单独使用(select * from User),会报错。
 Query< String> query=session.queryQuery("select u.name from User as u ");	//可以使用别名,可以查询到具体字段值。

(2)排序查询:(默认是升序asc)

String hql="from Student order by id desc"; //降序

(3)条件查询:

Query<User> query=session.createQuery("from User where id=?"); 

使用修改命名的方式:

 Query<Student> query=session.createQuery("from Student where sid= :a");
 query.setParameter("a",4);
  
 Query<Student> query=session.createQuery("from Student where name like :n and email= :e");
 query.setParameter("n","T%");
 query.setParameter("e","123@qq.com");

使用JPA占位符:

 Query<Student> query=session.createQuery("from Student where sid= ?0 and name= ?1");
 query.setParameter(0,2);
 query.setParameter(1,"Tom");
 
 Query<Student> query=session.createQuery("from Student where sid= ?1 and name= ?2");
 query.setParameter(1,1);
 query.setParameter(2,"Mary");

(4)投影查询

 Query<String> query =session.createQuery("select s.name from Student as s");	//查询单个属性
 Query<Object[]> query =session.createQuery("select s.name,s.email from Student as s");	//查询多个属性
 Query<Object[]> query =
            session.createQuery("select new Student(name,email) from Student ");	//查询多个属性,并将其封装成对象(前提是要提供对应的构造方法)

(5)分页查询

  Query<Student> query=session.createQuery("from Student ");
  query.setFirstResult(0);    //设置查询的起始值
  query.setMaxResults(5);     //设置查询的数量
  List<Student> list=query.list();

(6)分组统计查询

  long result=(long)session.createQuery("select count(*) from Student ").uniqueResult();	//uniqueResult()表示只有一个值,返回的是一个Object
  
    List<Object[]> list=
            session.createQuery("select school,count(*) from Student group by school having count(*)>1").list();
    for(Object[] objects:list){
        System.out.println(Arrays.toString(objects));
    }

注意:HQL中查询的是对象或属性,是一种面向对象的查询语言。

7、HQL多表查询

先回顾一下多表查询:
1、连接查询:

  • 交叉连接 (笛卡尔积):select * from A,B;
  • 内连接 :inner join (inner可以省略) , 交集
    (1)隐式内连接 : select * from A,B where A.cid=B.cid;
    (2)显式内连接 : select * from A inner join B on A.cid=B.cid;
  • 外连接
    (1)左连接 : left outer join(outer可以省略),查询的是左边表的全部信息和两个表的公共部分
    select * from A left join B on A.cid=B.cid
    (2)右连接 : right outer join(outer可以省略),查询的是右边表的全部信息和两个表的公共部分
    select * from A right join B on A.cid=B.cid
    2、子查询:
    select * from A where age=(select max(age) from B);

(1)HQL内连接:

String hql="from School s inner join s.students";
Query<Object[]> query=session.createQuery(hql);
 List<Object[]> list=query.list();

(2)HQL迫切内连接:加了一个关键字fetch,最好加一个distinct去重

 String hql="select distinct s from School s inner join fetch s.students";
 Query<School> query=session.createQuery(hql);
 List<School> list=query.list();

注意:两者不同之处是,内连接返回封装成Object数组,而迫切内连接封装成对象。

4、QBC检索
QBC(Query By Criteria),条件查询,是一种更加面向对象的查询方式。(使用一系列Hibernate中的方法来进行查询)

(1)简单查询

List<Student> list=session.createCriteria(Student.class).list();

(2)排序查询

 List<Student> list=session.createCriteria(Student.class).addOrder(Order.asc("sid")).list();
 // Order.asc("sid") 升序 ,Order.desc("sid") 降序

(3)分页查询

Criteria criteria=session.createCriteria(Student.class);
criteria.setFirstResult(0);
criteria.setMaxResults(2);
System.out.println(criteria.list().size());

(4)条件查询

 Criteria criteria=session.createCriteria(Student.class);
 //添加条件   Restrictions下还有很多条件的方法
 criteria.add(Restrictions.eq("name","Tom"));
 List<Student> list=criteria.list();

(5)统计查询

 Criteria criteria=session.createCriteria(Student.class);
        //设置统计的方法
 criteria.setProjection(Projections.max("age"));
 long maxAge=(long)criteria.uniqueResult();

(6)离线条件查询 DetachedCriteria (一般用于多条件查询)
一般是将离线条件DetachedCriteria放在web层或service层添加一系列的条件,然后传给DAO层,与session绑定,进行查询。

// 离线条件 DetachedCriteria
DetachedCriteria detachedCriteria=DetachedCriteria.forClass(Student.class);
            //添加条件


 detachedCriteria.add(Restrictions.eq("name","Tom"));
 detachedCriteria.add(Restrictions.gt("agel","18"));
    
 Session session= HibernateUtils.getCurrentSession(); 
 Transaction transaction=session.beginTransaction();
    
Criteria criteria=detachedCriteria.getExecutableCriteria(session);  //与session绑定
List<Student> list=criteria.list();
System.out.println(list.toString());

5、SQL检索

// 返回的记录封装成了Object数组
List<Object[]> list=session.createSQLQuery("select * from school").list();
for(Object[] objects:list){
      System.out.println(Arrays.toString(objects));
 }


// 使用方法 addEntiry() , 返回的记录封装在对象中
List<School> list=session.createSQLQuery("select * from school")
                         .addEntity(School.class)	//将记录封装成对象
                         .list();

    for(School school:list){
        System.out.println(school);
    }

十一、Hibernate的抓取策略(优化方式)
主要由< set>或者< many-to-one>上fetch和lazy的取值决定的。

  • fetch决定发送语句的格式。
  • lazy决定是否延迟加载。

延迟加载(懒加载):
(1)类级别的延迟加载:< class>标签上lazy ,主要体现在load方法的应用
(2)关联级别的延迟加载:< set>或者< many-to-one>上的lazy ,主要体现在查询其关联对象时

1、< set>上的fetch和lazy ,一般取默认值 :fetch=“select” lazy=“true”

  • fetch 的取值:
    (1)select :默认值,发送普通的select语句查询其关联对象
    (2)join :发送的是一条迫切左外连接查询其关联对象。(当取该值时,lazy失效)
    (3)subselect :发送一条子查询查询其关联对象
  • lazy 的取值:
    (1)true :默认值,查询关联对象时,延迟加载
    (2)false :查询关联对象时,不延迟加载
    (3)extra :比lazy="true"时更懒,需要什么才查询。

2、< many-to-one>上的fetch和lazy ,一般取默认值 : fetch=“select” lazy=“proxy”

  • fetch 的取值:
    (1)select :默认值,发送普通的select语句查询其关联对象
    (2)join :发送的是一条迫切左外连接查询其关联对象。(当取该值时,lazy失效)

  • lazy 的取值:
    (1) proxy :默认值,具体的取值取决于一的一方的< class>上的lazy取值
    (2)false :查询关联对象时,不延迟加载
    (3)no-proxy(基本不使用)

3、批量抓取
通过配置 batch-size=“每次批量抓取数量” 来实现批量抓取.

  • 批量抓取一的一方时,同时批量抓取多的一方 。在< set> 标签上配置 batch-size 。
  • 批量抓取多的一方时,同时批量抓取一的一方。在一的一方的< class>标签上配置batch-size
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值