• Hibernate概念初探


    概述


    Hibernate是一个开源代码的对象关系映射(ORM)框架,是基于Java的持久化中间件,它对JDBC进行轻量级的对象封装。

    它不仅提供了从Java类到数据表之间的映射,也提供了查询和事务机制。

    相对于使用JDBC和SQL操作数据库,Hibernate大大减少了操作数据库的工作量。

    作用


    作为持久化的中间件,Hibernate采用ORM映射机制,实现Java对象和关系数据库之间的映射,把SQL语句传给数据库,并且把数据库返回的结果封装成对象。

    内部封装了JDBC访问数据库的操作,向上层应用提供了面向对象的数据库访问API。从而使程序员使用面向对象的思维来操作数据库。

    如图示:

    两个重要概念


    1)数据持久化

    数据持久化就是把内存中的数据(对象)永久保存到数据库中,实际上数据“持久化”包括了与数据库相关的各种操作。比如,保存、更新、删除、查询和加载。

    说一下“加载”:

    根据特定对象OID,把一个对象从数据库中加载到内存中。为了在系统中能够找到所需的对象,需要为每一个对象分配一个唯一的标识号。关系型数据库中称之为主键,而在对象术语中则称为对象标识(Object identifier-OID)。

    数据持久化负责封装数据的访问操作,为业务逻辑提供面向对象的数据操作接口。

    在Hibernate框架中,提供了访问数据库的方法,在使用时候直接调用即可。

    2)ORM

    全称Object/Relation Mapping,即对象/关系映射,是一种数据持久化技术,其基本思想是把对象模型(如JavaBean对象)与关系数据库的表建立映射关系。

    在JDBC中,我们要做的是使用SQL语句操作数据库中的表;

    而在Hibernate中我们不再需要直接操作数据库,而是转化为了对JavaBean对象的操作,就可以实现数据的存储、查询、更改和删除等操作。

    ORM中对象与数据库表的映射关系
    面向对象概念 面向关系表(结构)概念
    数据表
    对象 数据表中的行(记录)
    属性 数据表中的列(字段)

    Hibernate框架的结构体系


    基于Hibernate的应用程序是通过配置文件(hibernate.properties或hibernate.cfg.xml)和映射文件(*.hbm.xml)把持久化对象(Persistence Object,PO)映射到数据库的数据表,然后通过操作持久化对象(PO),对数据库中的数据进行增删查改。

    由图中可以看出,使用Hibernate的开发人员的主要任务就是编写Hibernate配置文件(对应图中hibernate.properties)、设计PO类及对象-关系映射文件(对应图中XML Mapping),然后利用HibernateAPI来操作数据库。

    Hibernate的核心组件


    Hibernate全面解决方案体系架构:

    Hibernate组件层次架构:

    这些组件按被使用的次序分为5层,上层可对下层进行调用和使用。

    1)Hibernate配置文件:主要用来配置数据库连接参数。如数据库驱动程序、URL、用户名和密码等。

    它有两种格式:hibernate.properties和hibernate.cfg.xml。两者配置内容基本相同,通常使用后者。

    2)持久化对象(PO):可以是普通的JavaBean。

    3)映射文件:用来把PO与数据库中的数据表映射起来,是Hibernate的核心文件。

    4)Configuration类:用来读取Hibernate配置文件和映射文件,并创建SessionFactory对象。

    5)SessionFactory接口:产生Session实例的工厂,是Hibernate的容器。(该接口是线程安全的,可以被多个线程调用。因为构造SessionFactory很消耗资源,所以多数情况下应用中只初始化一个SessionFactory对象,为不同的线程提供Session。当客户端发送一个请求时,从SessionFactory中获取Session对象,由Session对象处理客户请求)

    6)Session接口:用来操作PO,它有get()、save()、update()、delete()等方法,用来对PO进行加载、保存、更新及删除等操作。它是Hibernate的核心接口。(持久化对象 的生命周期、事务管理,以及持久化对象的查询、更新和删除等操作都是通过Session对象完成)

    7)Transaction接口:用来管理Hibernate事务,主要的方法有commit()和rollback(),可从Session的beginTransaction()方法生成。

    8)Query接口:对PO进行查询操作。它可以从Session的createQuery()方法生成。

     Hibernate运行过程


    1)应用程序先调用Configuration类,该类读取配置文件和映射文件,根据这些信息生成SessionFactory对象。

    //创建Configuration对象:加载Hibernate的基本配置信息和对象关系映射
    Configuration configuration=new Configuration.configure();
    //创建一个ServiceRegistry对象:Hibernate 4.x新添加的对象,Hibernate的任何配置和服务都需要在该对象中注册后才能生效
    ServiceRegistry serviceRegistry=new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();

    2)每次执行数据库事务时,先从SessionFactory中获取一个Session实例。

    //创建SessionFactory对象
    SessionFactory  sessionFactory=configuration.buildSessionFactory(serviceRegistry);

    3)由Session对象启动事务,并生成Transaction对象。

    //创建一个Session
    Session session=sessionFactory.openSession();
    //开启事务
    Transaction transaction=session.beginTransaction();

    4)通过Session对象的方法对PO进行加载、保存、更新和删除等操作。在查询的情况下,通过Session对象生成Query对象,执行查询操作。

    //执行保存操作——通过实体对象实现对数据库表的操作,是数据库的核心
    //例如有一个实体类Book
    Book book=new Book();
    book.setBookId("1");
    book.setBookName("java编程思想");
    session.save(book);//通过对象实现向数据库中的数据表存放信息

    5)若没有异常,Transaction对象将提交操作结果到数据库中,否则事务将回滚。

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

    6)当事务操作完成后,关闭Session实例对象。

    //关闭Session对象
    session.close();

     设计PO类及对象-关系映射文件(*.hbm.xml)


    1)建立持久化对象模型(即实体类,包括相应的setter/getter方法),自不必多说;

    2)建立对象-关系映射文件(*.hbm.xml)。

    这里的*要与对应的持久化类类名相同,即“持久化类类名.hbm.xml”。且映射文件要求与持久化实体类在同一个包内。

    该文件给出了“实体类”与“数据库表”,以及“类属性”与“表字段”之间的映射关系。

    以实体类Person.java为例。

    右击Person.java选项,在弹出的快捷菜单中选择New——Other——Hibernate——Hibernate XML Mapping file(hbm.xml)命令。在弹出的对话框中单击Next按钮,选择类Person,单击Next按钮两次,再单击Finish按钮,即可生成映射文件。

    <hibernate-mapping>
    <!--实体类Person与数据库表PERSON之间的对应关系-->
    <class  name="com.entity.Person"  table="PERSON">
    <!--主键配置定义-->
    <id name="id" type="java.lang.Tnteger">
    <column name="ID"/>
    <!--设置主键生成的方式,该元素的作用是指定主键的生成器,通过class属性指定生成器对应的类,这里使用的Hibernate提供的常用内置生成器-->
    <generator class="native">
    </id>
    <!--配置一般属性与字段配置-->
    <property  name="name" type="java.lang.String">
    <column name="NAME"/>
    </property>
    <property  name="sex" type="java.lang.String">
    <column name="SEX"/>
    </property>
    <property  name="age" type="int">
    <column name="AGE"/>
    </property>
    </class>
    </hibernate-mapping>

    建立数据库配置文件hibernate.cfg.xml。


    该文件建立在src目录下。

    其配置信息:采用MySQL数据库为例,数据库的基本配置有:驱动类、数据库url、数据库操作用户名、密码、所采用数据库的方言(必须指定,表示连接的是哪种数据库)。

    同时利用映射文件信息,自动创建数据库表PERSON。

    具体步骤:

    右击src选项,在弹出的快捷菜单中选择New——Other——Hibernate——Hibernate Configuration File(cfg.xml)命令。在弹出的对话框中单击Next按钮两次,再单击Finish按钮,即可生成配置文件。

    <hibernate-configuration>
    <session-factory>
    <!--数据库基本配置-->
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="connection.url">jdbc:mysql:///javaee数据库名</property>
    <property name="connection.username">root</property>
    <property name="connection.password">123456</property>
    <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
    
    <!--非数据库基本配置-->
    <!--表示可由类和映射文件自动生成数据库表-->
    <property name="hbm2ddl.auto">update</property>
    <!--显示运行中执行的sql语句,一般调试时设置为true,便于观察代码执行的SQL语句的细节-->
    <property name="show_sql">true</property>
    <!--在控制台按格式显示SQL语句-->
    <property name="format_sql">true</property>
    
    <!--配置实体类的映射文件,指定路径名,可以配置多组-->
    <mapping resource="com/entity/Person.hbm.xml"/>
    </session-factory>
    </hibernate-configuration>

    Hibernate 核心组件详解


    1)Hibernate配置文件。

    主要用来设置访问数据库所需要的参数,最基本的有3类:

    1.JDBC连接参数的基本配置。

    2.Hibernate连接池的参数配置(可以不配置,但在实际应用项目中必须配置,以提高系统性能)

    3.注册ORM映射文件的配置参数。

    在Hibernate中使用C3P0数据源来支持连接池。

    2)Hibernate的PO对象

    原则:

    1.无参构造方法(必须);

    2.提供一个标识属性(OID):通常映射为数据库表的主键字段;

    3.每个属性都有getter/setter方法

    Hibernate的PO的状态及转换条件

    与普通的JavaBean对象不同的是PO对象与Session(缓存)相关联。PO在Hibernate中存在下列4种状态。

    1.瞬时(Transient):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器回收,一般是new创建,且与session没有关联的对象,OID为0或null。

    2.持久(Persistent):数据库中有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提交。

    3.删除(Removed):在数据库中没有与OID对应的记录,不再处于Session缓存中,一般情况下,应用程序不该再使用被删除的对象。

    4.脱管(Detached):数据库中有数据与之对应,但当前没有session与之关联。

    3)Hibernate的映射文件

    Hibernate的映射文件把一个PO类和数据库表联系起来。通常一个PO类对应一个映射文件,其命名规范为:PO类名称.hbm.xml,且与PO类在同一包下。

    主键使用<id>标签来配置。Hibernate使用OID(对象标识符)来标识对象的唯一性。在运行时,Hibernate根据OID来维持Java对象和数据库中记录的对应关系。

    HQL语言与Query接口应用


    1)Hibernate提供了一种功能强大的查询语言——HQL(Hibernate  Query  Language),直接使用实体类名及属性实现查询。

    与SQL语言类似。只不过SQL是针对表中的字段进行查询,而HQL是一种完全面向对象的语言,能够直接查询实体类及属性。

    语法与SQL语言类似。是一种“select...from...”的结构。其中,from后面跟的是实体类名,而不是表名。select后面跟的可以是实体对象,也可以是实体对象的属性或者其他的值。

    String  hql="select  person.name  from  Person  as  persons";
    //查询所有的Person
    //创建Query对象
    Query  query =session.createQuery(hql);
    //执行查询,返回List
    List  list=query.list();

    2)设置分页

    分页是查询中最常用的功能,当查询结果很多时,通常要将查询结果分页显示。

    在Hibernate中,查询结果的分页主要是通过以下方法来实现的。

    setFirstResult();//设置起始位置(索引位置是从0开始)

    setMaxResults();//设置返回最大记录数

    Hibernate的QBC查询


    当查询数据时,通常都需要设置查询条件,例如在SQL语句或HQL语句中,查询条件常常放在where子句中,当查询参数较多时,使用SQL语句或HQL语句过长、复杂、极易出错。

    为此,Hibernate把查询条件封装为一个Criteria对象,基于Criteria对象实现查询——Query By  Criteria查询(QBC查询)
    //创建查询条件的查询对象
    Crieria cr=session.createCriteria(student.class);
    //向查询对象中添加查询条件:性别sex为“男”,可以连续添加多个查询条件
    cr.add(Restrictions.eq("sex","男"));
    //实现查询,获取查询对象集合studentList
    List<student> studentList=cr.list();

    Hibernate的Native SQL查询


    Hibernate提供的Native SQL,可以使用标准的SQL语句实现对数据库的操作。

    String sql="select  * from t_studnet";
    //创建SQLQuery实例
    SQLQuery sqlQuery=session.createSQLQuery(sql);
    //还需要传入查询的实体类
    sqlQuery.addEntity(student.calss);
    //实现查询,获得查询结果集合
    List<Student> list=sqlQuery.list();

    Hibernate实体关联关系映射


    在一个应用系统中通常有多个实体,并且实体之间存在互相关联。

    实体关系要从方向性和数量性两个方面来考虑。

    实体间关系从方向上可分为单向关联和双向关联。单向关联只需在一方进行映射配置,而双向关联则需要在关联的双方进行映射配置。

    实体间的关系从引用的数量上分为:一对一、一对多、多对一、多对多。

    实体之间的关联关系的实现方式有:基于外键、基于主键和基于关联表的实现。

    实体之间的各种关联关系分类:

     

    1)一对一映射

    例子:一个人拥有一个身份证。

    一对一关系在Hibernate中的实现有两种方式,分别是主键关联和外键关联,并且既可以是单向关联,也可以是双向关联。

    1.共享主键方式双向关联

    就是两个相关联的数据表,使用相同的主键实现映射。

    在实体关系中,由于双向关联,每个实体类都有对另一方的对象引用。

    在数据表关系中,两个表的主键名称一样(主键名称也可以不同),但对应记录的两个主键取值是一样的(由一方产生主键值,然后提供给另一方的主键值)

    由于两个表要共享主键,那么在映射时,如何创建主键,由谁来创建?在Hibernate的映射文件中使用主键的foreign生成机制。

    (以Person(人)和IdCard(身份证)为例)

    在本例中,主键由Person产生,然后,利用foreign生成机制,赋值给IdCard的主键,从而保证两个主键具有相同的值。

    package  com.entity.one2one;
    
    public class Person{
    
    private int id;
    private String name;
    private IdCard idCard;//与另一方关联,需要使用对方的对象属性
    }
    package  com.entity.one2one;
    
    public class IdCard{
    
    private int id;
    private String cardNo;
    private Person person;//与另一方关联,需要使用对方的对象属性
    }

    Person映射文件:

    <hibernate-mapping>
    <!--lazy="true"是配置延迟加载-->
    <class name="com.entity.one2one.Person" lazy="true">
    
    <id name="id" type="int">
    <column name="ID"/>
    <generator class="native"/>
    </id>
    
    <property name="name"/>
    
    <!--双向关联,需要在两个配置文件中都配置one-to-one-->
    <!--通过cascade="all"配置了级联操作,当“人”更新时,自动对“身份证”信息进行更新-->
    <one-to-one name="idCard" class="com.entity.one2one.IdCard" fetch="join"  cascade="all">
    </one-to-one>
    
    </class>
    </hibernate-mapping>

    说明,

    代码中配置了延迟加载,若其值配置为false,则表示立即加载。

        立即加载:表示Hibernate在从数据库中取得数据,组装成一个对象后,会立即再从数据库取得所关联的对象。

        延迟加载:表示Hibernate在从数据库中取得数据,组装成一个对象后,不会立即再从数据库取得所关联的对象,而是等到需要时再从数据库取得关联对象。

    cascade属性表明操作是否从父对象级联到被关联的对象(称为“级联”)。

    fetch属性的值是join或select,默认值是select。

        当fetch="join"时,表示连接抓取(Join fetching):Hibernate通过在SELECT语句使用OUTER JOIN(外连接)来获取对象的关联实例或者关联集合。

        当fetch="select"时,表示查询抓取(Select fetching):需要另外发送一条SELECT语句抓取当前对象的关联实体或集合。

        当cascade="all",表示增加、删除及修改Student对象时,都会级联增加、删除和修改Card对象。

    Id_Card映射文件:

    <class name="com.entity.one2one.Id_Card"  table="IDCARD" lazy="true">
    
    <id name="id" type="int">
    <column name="ID"/>
    <!--主键id使用外键(foreign)生成机制,引用代号为person对象的主键作为IdCard表的主键和外键-->
    <generator class="foreign">
    <param name="property">person</param>
    </generator>
    
    </id>
    
    <property name="cardNo"/>
    <!--person在该标签中进行了定义-->
    <!--constrained="true"表示IdCard引用了person的主键作为外键-->
    <one-to-one  name="person" class="com.entity.one2one.Person" constrained="true">
    </one-to-one>
    
    </class>

    2.基于外键实现一对一双向关联

    要点:两个数据表各自有不同的主键,但其中一个数据表包含有一个新字段——对另一个数据表主键的引用(外键)。

    这种一对一的关系其实就是多对一关系的一种特殊情况(外键取值是唯一的)

    (本部分未给出代码和上面实例一致)

    Id_Card映射文件:

    对于Id_Card的映射,实际上要添加外键字段,该外键就是Person的主键。在配置映射时,将实体类Id_Card中的person,对应于实体类Person的主键即可。

    <many-to-one  name="person"  class="com.entity.one2one.Person" column="person_id" unique="true">
    
    </many-to-one>

    2)多对一单向外键映射

    其中,“多方”是主动方,“一方”是被动方,而“多方”的外键是由“一方”产生并提供给“多方”的。

    (以Employee(雇员)和Department(部门)为例)

        两个实体类之间关系:在“多方(employee)”中必须含有对“一方(Department)”的对象的引用,才能实现多对一的单向关联。

        两个数据表之间的关系:采用外键关联,且外键字段为depart_id。

    实现的关键:

        在“多方(Employee)”含有对“一方(Department)”的对象引用。

        在“多方(Employee)”的映射文件内,使用<many-to-one>标记进行关联关系映射并制定外键。

    <many-to-one name="depart" class="com.entity.Department"  fetch="join">
    <column name="depart_id"/>
    </many-to-one>

    3)一对多单向外键映射

    其中,“一方”是主动方,“多方”是被动方,而“多方”的外键是由“一方”产生并提供给“多方”的。

    (以Employee(雇员)和Department(部门)为例)(一个部门里有多个雇员)

    实现关键:

        在“一方(Department)”有对“多方(Employee)”集合类型的引用。

    例:

    public  class  Department{
    //...
    private  Set<Employee>  employees;
    }

        由于“一方(Department)”是主动方,需要在Department的映射文件中使用<set>、<one-to-many> 和<key>标记进行映射,给出多方的相关联的外键。

    在Department映射文件中,由于Department是主动方,需要在该映射文件中使用<one-to-many>标记进行映射,给出相关联的外键。

    <set name="employees" lazy="true">
    <key><column name="DEPART_ID"></key>
    <one-to-many class="com.entity.one2many.Employee"/>
    </set>

    说明,

    使用<key>指定关联外键。

    4)一对多双向外键映射

    “一对多双向外键映射”与“多对一双向外键映射”一样,因为双方既是主动方也是被动方。双方都需要给出映射关系的配置,并制定主控方,用于控制“外键”。

    (以Employee(雇员)和Department(部门)为例)

    实现关键:

        在“一方(Department)”有对“多方(Employee)”集合类型的引用。

        在“多方(Employee)”有对“一方(Department)”对象的引用。

        双方都需要给出映射关系的配置,并制定主控方,用于控制“外键”。

    在Employee映射文件中:

    <many-to-one name="depart" class="com.entity.Department" fetch="join">
    <column name="DEPART_ID"/>
    </many-to-one>

    在Department映射文件中:

    <set name="employees" table="EMPLOYEE" inverse="true" lazy="true">
    
    <key>
    <column name="DEPART_ID"/>
    </key>
    
    <one-to-many  class="com.entity.Emplyee"/>
    </set>

    说明,

    inverse="false"

    inverse属性用在”一对多“和”多对多“双向关联上,inverse默认为false,表示本端可以维护关系,若inverse为true,则本端不能维护关系,交给另一端维护关系。

    5)多对多映射

    需要通过”关联表“实现。

    (以学生(Student)选课(Course)为例)

    实体类之间的关系:在Student中必须含有对Course的对象集合的引用;在Course中必须含有对Student的对象集合的引用,这样才能实现双向关联。

    数据表之间的关系:使用连接表(Student_Course)实现,Student_Course表包含两个字段:CourseId和StuId。

    实现关键:

        两个实体类的设计,在一方都含有另一方的对象集合的引用。

        因为双方既是主动方也是被动方。双方都需要给出映射关系的配置,并制定主控方,用于控制“外键”。

    Student实体类的映射文件Student.hbm.xml。

    <!--使用关联表Student_Course-->
    <!--配置inverse="false"-->
    <set name="course" table="Student_Course" inverse="false" lazy="true">
    <!--使用<key>指定关联表外键:StuId-->
    <key><column name="StuId"></key>
    <many-to-many class="com.entity.Course" column="CourseId"/>
    </set>

    Course实体类的映射文件Course.hbm.xml。

    <!--使用关联表Student_Course-->
    <!--配置inverse="true"-->
    <set name="students" table="Student_Course" inverse="true" lazy="true">
    <!--使用<key>指定关联表外键:CourseId-->
    <key><column name="CourseId"></key>
    <many-to-many class="com.entity.Student" column="StuId"/>
    </set>

    使用inverse指定Student为关联对象的主控方。

  • 相关阅读:
    python 不可变类型
    python 不定长参数
    Codeforces Round #749
    [提高组集训2021] Round1
    AtCoder Regular Contest 128
    [提高组互测] Day6
    [提高组互测] Day5
    [提高组互测] Day1
    [提高组互测] Day2
    [提高组集训2021] 一拳超人
  • 原文地址:https://www.cnblogs.com/dudududu/p/8592548.html
Copyright © 2020-2023  润新知