Hibernate-基本原理

序言

Hibernate,翻译过来是冬眠的意思,其实对于对象来说就是持久化。持久化是将程序数据在持久状态和瞬时状态间转换的机制。JDBC就是一种持久化机制。文件IO也是一种持久化机制。
  
  Hibernate是一个开放源代码的对象关系映射(ORM)框架,它对JDBC进行了非常轻量级的对象封装,它将POJO(JavaBean对象)与数据库表建立映射关系,是一个全自动的ORM框架Hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用。ORM框架通过配置文件将实体对象和数据库表对应起来。Hibernate框架使用在数据持久化层(dao)


1、Hibernate基础

(1) 什么是ORM

对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。对象-关系映射,是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射

ORM模型的简单性简化了数据库查询过程。使用ORM查询工具,用户可以访问期望数据,而不必理解数据库的底层结构。
这里写图片描述
简单来说,我们使用ORM可以将我们的对象(或类)去进行映射,使得我们可以去操作对象就能完成对表的操作

(2) 为什么使用Hibernate框架

原因如下:

1、Hibernate对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
2、Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现,它很大程度的简化了dao层编码工作。

总结:Hibernate是企业级开发中的主流框架,映射的灵活性很出色。它支持很多关系型数据库。

(3) Hibernate的体系结构

这里写图片描述

(4) Hibernate的开发步骤

Hibernate开发步骤如下:

1、创建持久化类
2、创建对象-关系映射文件
3、创建Hibernate配置文件
4、通过Hibernate API编写访问数据库的代码

如下图:
这里写图片描述


2、入门实例

前提准备:导入jar包,需要的jar包:

hibernate路径下下lib/required的所有jar包和jpa目录下的jar包
mysql驱动jar包

(1) 创建实体类

Hibernate要求实体类必须提供一个不带参数的默认构造方法。因为程序运行时,Hibernate会运用java的反射机制,创建实体类的实例

public class User {
      private int id;   
      private String name;
      private String password;
      public int getId() {
           return id;
      }
      public void setId(int id) {
           this.id = id;
      }
      public String getName() {
            return name;
      }
      public void setName(String name) {
            this.name = name;
      }
      public String getPassword() {
            return password;
      }
      public void setPassword(String password) {
            this.password = password;
       }
}

(2) 创建对应的数据库表

咱们使用Mysql数据库,建表如下:

CREATE DATABASE hibernate;
USE hibernate;
CREATE TABLE USER(
   id INT PRIMARY KEY AUTO_INCREMENT,
   name VARCHAR(50),
   password VARCHAR(50) 
);

(3) 创建Hibernate的配置文件

放在src目录下,文件名为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.username">root</property>//数据库的用户名
    <property name="hibernate.connection.password">123456</property>//数据库的密码
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property>//连接数据库的url

    <property name="show_sql">true</property>//为true时会在控制台输出sql语句,有利于跟踪hibernate的状态
    <property name="format_sql">true</property>//会格式化输出sql语句
    <property name="hbm2ddl.auto">update</property>
    <property name="hibernate.connection.autocommit">true</property>//自动提交事务

    <mapping resource="com/cad/domain/User.hbm.xml"/>//引入映射文件

</session-factory>
</hibernate-configuration> 

(4) 创建对象-关系映射文件

该文件应该和实体类在同一目录下。命名规则为 实体类名.hbm.xml 例如:User.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.cad.domain.User" table="user"> //指定实体类和对应的数据表
    <id name="id" column="id">                 //<id>元素设定类中的id和表的主键id的映射
        <generator class="native"></generator> //<gender>元素指定对象标识符生成器,负责生成唯一id。详情见后面。
    </id>
    <property name="name" column="name"></property> //name是实体类属性的名字,column是数据表中列的名字
    <property name="password" column="password"></property>
</class>
</hibernate-mapping>    

(5) 调用HibernateAPI操作数据库

进行测试:

public class Demo {
     @Test
     public void test() {
          //读取配置文件
          Configuration conf=new Configuration().configure();
          //根据配置创建factory
          SessionFactory sessionfactory=conf.buildSessionFactory();
          //获得操作数据库的session对象
          Session session=sessionfactory.openSession(); 
          //创建对象
          User u=new User();
          u.setName("张三");
          u.setPassword("123456");
          //使用Hibernate操作数据库,都要开启事务,得到事务对象
          Transaction transaction = session.getTransaction();
          //开启事务
          transaction.begin();
          //将对象保存到数据库
          session.save(u);
          //提交事务
          transaction.commit();
          //关闭资源
          session.close();
          sessionfactory.close();
     }
}

通过上面的小例子,我们大致了解了使用Hibernate的流程,接下来让我们详解Hibernate。

Hibernate执行过程总结:

1、通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
2、由hibernate.cfg.xml中的<mapping resource="com/xx/Xxx.hbm.xml"/>读取解析映射信息。
3、通过config.buildSessionFactory();得到sessionFactory。
4、sessionFactory.openSession();得到session。
5、session.beginTransaction();开启事务。
6、persistent operate; 执行你自己的操作。
7、session.getTransaction().commit();提交事务。
8、关闭session。
9、关闭sessionFactory。

3、详解Hibernate框架配置

(1) 核心配置文件

Hibernate从其配置文件中读取和数据库连接有关的信息。Hibernate配置文件有两种形式,XML格式 或者 Java属性文件(properties)格式

1、Java属性文件格式

属性文件的格式创建hibernate的配置文件,默认文件名为hibernate.properties,为键值对的形式,放在src目录下:例如:

hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbv:mysql://localhost:3306/hibernate
hibernate.connection.username=root
hibernate.connection.password=123456
hibernate.show_sql=true

解释:
1、hibernate.dialect:指定数据库使用的sql方言。可以根据数据库的不同生成不同的方言。
2、hibernate.connection.driver_class:指定数据库的驱动程序
3、hibernate.connection.url:指定连接数据库的url
4、hibernate.connection.username:指定连接数据库的用户名
5、hibernate.connection.password:指定连接数据库的密码
6、hibernate.show_sql:如果为true,可以在控制台打印sql语句 

7、hbm2ddl.auto:生成表结构的策略配置,配置这个可以通过映射文件和实体类自动生成表结构
有四个值:
(1)update(最常用的取值):如果当前数据库不存在对应的数据表,那么自动创建数据表
       A、如果存在对应的数据表,并且表结构和实体类属性一致,那么不做任何修改
       B、如果存在对应的数据表,但是表结构和实体类属性不一致,那么会新创建与实体类属性对应的列,其他列不变 
(2)create(很少使用):无论是否存在对应的数据表,每次启动Hibernate都会重新创建对应的数据表,以前的数据会丢失
(3)create-drop(极少使用):无论是否存在对应的数据表,每次启动Hibernate都会重新创建对应的数据表,每次运行结束删除数据表
(4)validate(很少使用):只校验表结构是否和我们的实体类属性相同,不同就抛异常 

2、XML格式配置文件

使用xml格式的配置文件,默认文件名为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.username">root</property>
       <property name="hibernate.connection.password">123456</property>
       <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property>

       <property name="show_sql">true</property>
       <property name="format_sql">true</property>
       <property name="hbm2ddl.auto">update</property>
       <property name="hibernate.connection.autocommit">true</property> 

       //引入映射文件
       <mapping resource="com/cad/domain/User.hbm.xml"/>

  </session-factory>
</hibernate-configuration>

两种方式的区别

1、加载映射文件的方式不同
   (1) 如果Hibernate的配置文件为Java属性文件,那么必须通过代码来声明需要加载的映射文件,即通过Configuration的addClass(实体类名.class)来加载。
   (2) 而配置文件为xml文件时,可以只通过<mapping>元素来指定需要加载的映射文件。
2、Configuration的默认构造方法
   (1) 当通过Configuration的默认构造方法创建实例时,会默认查找hibernate.properties文件,如果找到就将配置信息加载到内存中。
   (2) 默认情况下,hibernate不会加载hibernate.cfg.xml文件,必须通过Configuration的configure()方法来显式加载hibernate.cfg.xml文件 

(2) 持久化类编写规范

如下:

1、必须提供无参数的默认构造方法。因为程序运行时,Hibernate会运用java的反射机制,创建实体类的实例。 

2、所有属性必须提供public访问控制符的set、get方法。

3、属性应尽量使用基本数据类型的包装类型(如Integer)
       基本数据类型无法表达null值,所有基本数据类型的默认值都不是null,这样就有很大的缺陷。
   例如有一个score属性,表示学生分数,如果为0,那么是表示该学生未参加考试还是说该学生成绩为
   0呢?这时候如果用包装类型,就可以使用null来表示空值,学生未参加考试等等。

4、不要用final修饰实体(将无法生成代理对象进行优化)

(3) 对象标识符

在关系数据库中,通过主键来识别记录并保证记录的唯一性。主键的要求:不允许为null,不能重复,不能改变。

主键一般分为两类:

(1) 自然主键:在业务中,某个属性符合主键的三个要求,那么该属性可以作为主键。比如人的身份证就可以当作主键。
(2) 代理主键:增加一个不具备任何意义的字段,通常为ID,来作为主键。

在Java中,按照内存地址不同区分不同的对象。在Hibernate中通过对象标识符(OID)来维持Java对象和数据库表中对应的记录。与表的代理主键对应,OID也是整数类型,为了保证OID的唯一性和不可变性,通常由Hibernate或者底层数据库库给OID赋值
   如果在映射关系中配置JavaBean的代理主键(如Id)是自动增长的,它只能是int类型,因为String类型是不能自动增长的。如果是你设置了String类型,又使用了自动增长,那么就会报出错误!

(4) 对象-关系映射文件

Hiernate采用XML格式的文件来指定对象和关系数据之间的映射。Hibernate通过这个文件来生成各种sql语句。命名规则为 实体类名.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">

 <!--
     根元素,有一常用属性为package,当指定了package时,后面的类名字就可以简短,
     例如 package="com.cad.domain",后面class标签的name只用写User即可
 --> 
 <hibernate-mapping>
    <!--class标签指定类和表的对应关系,name为类名,table为表名-->
    <class name="com.cad.domain.User" table="user"> 
        <!--
            id标签用来设定持久化类中的OID和表的主键的映射,
            name为持久化类中的属性,column是数据表中的主键列名
        -->
        <id name="id" column="id">
            <!--generator标签用来设定主键生成策略-->
            <generator class="native"></generator>
        </id>      
        <!--property 标签属性  -->
        <property name="name" column="name"></property>
        <property name="password" column="password"></property>
    </class>
 </hibernate-mapping>   

A、class标签

class标签指定类和表的对应关系,name为类名,table为表名。

(1) class标签的属性 dynamic-insert属性,默认是false,当我们执行插入语句时,会动态生成sql语句,
如果我们只为某个字段赋值,其他字段为null,但是生成的sql语句还是包含其他字段,例如user有两个属性,
我们只为name赋值,生成的sql语句还是 insert into user(name,password)values (?,?),
而当我们将该值设置为true时,生成的sql语句会仅包含不为null的字段,生成的sql语句就是
insert into user(name) values (?) 

(2)class标签的属性 dynamic-update属性,默认是false,当我们执行更新语句时,会动态生成sql语句,
如果我们只为某个字段更新,其他字段不变,生成的sql语句还是包含其他字段。而当我们将该值设置为true时,
生成的sql语句仅包含需要更新的字段。

使用上面的两个属性可以提高运行性能,但是Hibernate动态生成sql语句需要的性能很小,所以可以省略。

B、id标签

id标签用来设定持久化类中的OID和表的主键的映射,name为持久化类中的属性,column是数据表中的主键列名。

1、id标签的属性:length 指定列的数据长度;
2、id标签的属性:unsaved-value 指定当主键为某个值时,当做null来处理;
3、id标签的属性:access 也可用在<property>标签上 默认值为property,即通过相应的get set方法来访问持久化类的属性,
               当值为field时,表明使用反射机制直接访问类的属性,不推荐使用,破坏封装性

C、generator标签

generator标签用来设定主键生成策略,hibernate内置的几种主键生成策略:

1.increment 适用于代理主键。由Hibernate自动以递增的方式生成主键,每次增量为1 ,会执行两个sql语句,先从表中查找出最大的id,然后加一,插入当前数据
2.identity  适用于代理主键。由底层数据库生成主键,依赖数据库的主键自增功能
3.sequence  适用于代理主键。由底层数据库的序列来生成主键,前提是数据库支持序列。(mysql不支持,oracle支持)
4.hilo      适用于代理主键。Hibernate根据hilo算法来自己生成主键。
5.native    适用于代理主键。根据底层数据库对自动生成主键的支持能力选择 identity|sequence|hilo
6.uuid      适用于代理主键。采用UUID算法生成主键。
7.assigned  适用于自然主键。由我们自己指定主键值。例如指定身份证号为主键值            

D、 property 标签

property 标签属性:

name     指定持久化类的属性名称
column   与类属性映射的字段名,如果没有设置,默认用类属性名作为字段名
not-null 指定属性的约束是否为非空,默认false
unique   指定属性的约束是否唯一
type     指定Hibernate映射类型。例如java类型为string,数据库类型为text,那么应该把Hibernate类型设置为Text。
         有一张对应的表可以查看。如果没有指定映射类型,Hibernate会使用反射机制识别属性的java类型,然后自动使用
         与之对应的Hibernate映射类型。
              

4、Hibernate API详解

Hibernate的核心:
这里写图片描述
接口简述:

1、Configuration接口:     负责配置并启动Hibernate
2、SessionFactory接口:    负责初始化Hibernate
3、Session接口:           负责持久化对象的CRUD操作
4、Transaction接口:       负责事务处理
5、Query接口和Criteria接口:负责执行各种数据库查询

注意:Configuration实例是一个启动期间的对象,一旦SessionFactory创建完成它就被丢弃了。

(1) Configuration类

Configuration类:用来加载默认文件路径下的配置文件(hibernate.properties)。

调用configure()方法会加载默认文件路径下的xml格式的配置文(hibernate.cfg.xml),推荐使用。

如果配置文件在不默认文件路径下或者配置文件名不符合默认规则,可以使用:
(1) new Configuration().configure(file)  加载指定文件
(2) new Configuration().configure(path)  加载指定路径下的文件 

如果使用properties格式的配置文件,可以使用addClass(实体类名.class)方法可以加载映射文件。           

(2) SessionFactory对象

SessionFactory代表数据库存储源。根据Hibernate配置文件创建对应的数据库存储源。

//获取SessionFactory对象的方法:
new Configuration().configure().buildSessionFactory();

SessionFactory对象的特点:

(1) 一个 SessionFactory 实例对应一个数据库,应用从该对象中获得 Session 实例。
(2) SessionFactory对象创建后,和Configuration对象再无关联。修改Configuration包含的配置文件信息,不会对SessionFactory有任何影响。   
(3) 对象的缓存很大,就称为重量级对象。SessionFactory存放了Hibernate配置信息,映射元数据信息等,是重量级对象。
(4) SessionFactory 是线程安全的,意味着它的一个实例可以被应用的多个线程共享。
(5) SessionFactory 需要一个较大的缓存,用来存放预定义的SQL语句及实体的映射信息。另外可以配置一个缓存插件,这个插件被称之为 Hibernate 的二级缓存,被多线程所共享。

(3) Session对象

Session对象:代表程序和数据库的会话。

特点

1、Session 是应用程序与数据库之间交互操作的一个单线程对象,是 Hibernate 运作的中心。
2、Session 提供了操作数据库的各种方法,比如添加、修改、删除、加载和查询实体对象,被称为持久化管理器。
3、Session 是轻量级的,它的创建和销毁不会消耗太多的资源。应为每次客户请求分配独立的 Session 实例。
4、Session 是线程不安全的,应避免多个线程使用同一个 Session 实例。
5、持久化类与 Session 关联起来后就具有了持久化的能力,所有持久化对象必须在 session 的管理下才可以进行持久化操作。
6、Session 有一个缓存,被称之为 Hibernate 的一级缓存。每个 Session 实例都有自己的缓存。

1、获取Session对象

factory.openSession(): 获取新的Session实例。
factory.getCurrentSession():采用该方法创建的Session会取出当前线程中的Session,底层使用ThreadLocal进行存取。

(1)更新操作API

1、session.save(obj); 【保存一个对象】
2、session.update(obj); 【更新一个对象】
3、session.saveOrUpdate(obj); 【保存或者更新的方法】
  (1)没有设置主键,执行保存;
  (2)有设置主键,执行更新操作;
  (3)如果设置主键不存在则报错!

(2)主键查询

通过主键来查询数据库的记录,从而返回一个JavaBean对象

session.get(javaBean.class, int id);  【传入对应的class和id就可以查询】
session.load(javaBean.class, int id); 【支持懒加载】

2、save()方法:把Java对象保存到数据库中

Transaction ts=session.beginTransaction();
User u=new User();
u.setName("赵六");
u.setPassword("123456");
//将对象保存到数据库
session.save(u); 
ts.commit();

3、update()方法:更新数据库的方法

Transaction ts=session.beginTransaction();
//先查出要修改的对象,根据主键值   
User user=session.get(User.class, 1);
user.setName("jery");
//将对象更新到数据库,根据OID
session.update(user); 
ts.commit();

4、delete()方法:删除方法

底层根据OID进行删除,有两种方式:
(1)
Transaction ts=session.beginTransaction();
User user=session.get(User.class, 1); 
//删除指定对象
session.delete(user);   
ts.commit();
(2)
Transaction ts=session.beginTransaction();
User user=new User();
user.setId(2);
session.delete(user);
ts.commit();     

5、load()或get()方法:从数据库查找指定对象

session.get(实体类名.class,OID);
session.load(实体类名.class,OID);

load()和get()的区别
1、我们使用get查询时发现控制台会立马打出查询语句,而使用load查询时控制台不会打印查询语句。
2、get方法被调用时立刻发送sql语句到数据库进行查询。load方法被调用时并没有查询数据库,当我们需要使用查询的对象时,才去查询,所以当我
们打印对象时,才会在控制台打印sql语句。

get()的原理 
   程序调用get方法,Hibernate发送sql语句到数据库
   数据库返回结果,Hibernate将结果封装成对象,返回对象到程序。

load()的原理
   程序调用load方法,Hibernate使用代理技术,创建一个代理对象,属性只有ID值。
   然后返回代理对象给程序,我们使用对象时,代理对象调用Hibernate查询数据库,初始化其他属性。 

load方法,返回一个代理对象,获取其属性时,会查询数据库,每次访问属性都会查询数据库么?
答:不是。代理对象中有一个标识是否被初始化的boolean类型变量,记录是否被初始化。

(4) Transaction对象

封装了事务的操作。我们做增删改查等操作时,必须开启事务。
因为session是线程不安全的,这样主要是为了线程安全。保证数据的正确性。

1、开启事务:Transaction ts=session.beginTransaction();
2、提交事务:ts.commit();
3、回滚事务:ts.rollback();

   当通过getCurrentSession获取当前线程绑定的Session时,事务关闭时,会自动把Session关闭并删除。

(5) Query对象

封装HQL语句的对象。

返回一个对象的方法 query.uniqueResult();

分页相关:
query.setFirstResult(index):从第几个取
query.setMaxResults(count):指定取几行记录

(6) 查询所有对象的方法

第一种方式
使用HQL语言(后面会详细介绍),HQL语言是面向对象的
Query query=session.createQuery("from User");

第二种方式
Criteria c=session.createCriteria(User.class);
List<User> l=c.list();

第三种方式,使用原生sql语句进行查询
SQLQuery query=session.createSQLQuery("select * from user");
List l=query.list();
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页